| title | Алгоритм Манакера | |
|---|---|---|
| authors |
|
|
| created | 2019 | |
| weight | 3 |
Пусть есть строка
Мы сразу сталкиваемся с очевидной трудностью: их в строке может быть
Наивное решение — перебрать
vector<int> pal_array(string s) {
int n = s.size();
// окружим строку спецсимволами, чтобы не рассматривать выход за границы
s = "#" + s + "$";
// в этом массиве будем хранить расстояние от центра до границы палиндрома
vector<int> r(n, 0);
for(int i = 1; i <= n; i++)
while (s[i - r[i - 1]] == s[i + r[i - 1]])
r[i-1]++;
return r;
}Тот же пример
Для оптимизации применим идею, знакомую из алгоритма z-функции: при инициализации
vector<int> manacher_odd(string s) {
int n = (int) s.size();
vector<int> d(n, 1);
int l = 0, r = 0;
for (int i = 1; i < n; i++) {
if (i < r)
d[i] = min(r - i + 1, d[l + r - i]);
while (i - d[i] >= 0 && i + d[i] < n && s[i - d[i]] == s[i + d[i]])
d[i]++;
if (i + d[i] - 1 > r)
l = i - d[i] + 1, r = i + d[i] - 1;
}
return d;
}Так же, как и z-функция, алгоритм работает за линейное время: цикл while запускается только когда
Для случая чётных палиндромов меняется только индексация:
vector<int> manacher_even(string s) {
int n = (int) s.size();
vector<int> d(n, 0);
int l = -1, r = -1;
for (int i = 0; i < n - 1; i++) {
if (i < r)
d[i] = min(r - i, d[l + r - i - 1]);
while (i - d[i] >= 0 && i + d[i] + 1 < n && s[i - d[i]] == s[i + d[i] + 1])
d[i]++;
if (i + d[i] > r)
l = i - d[i] + 1, r = i + d[i];
}
return d;
}Также можно было не писать отдельно две реализации, а воспользоваться следующим трюком — сделать замену:
Теперь нечётные палиндромы с центром в # — чётным.