Manacher Algorithm
Manacher Algorithm
Gleen K. Manacher (1975), "A new linear-time 'on-line' algorithm for finding the smallest initial palindrome of a string"
一种关于一个字符串
根据回文串的性质可以知道
朴素
在求字符
对于
容易看出, 这样最坏的复杂度可达 aaaa...aaaa 时可以出现.
优化
还是先考虑
由于朴素算法外层循环是从小到大按顺序的, 所以可以认为求
设当前
i > Frontier + {f_1}_{Frontier} - 1
这时的
i \leq Frontier + {f_1}_{Frontier} - 1
这时的
因为
这时
这时, 可以断定
接下来, 考虑
-
Reverse - {f_1}_{Reverse} + 1 < Frontier - {f_1}_{Frontier} + 1 这时如果
{f_1}_i > Reverse - PalindromeL + 1 , 则s_{Frontier + {f_1}_{Frontier}} = s_{2i - Frontier - {f_1}_{Frontier}} = s_{3Frontier - 2i + {f_1}_{Frontier}} . 字符3Frontier - 2i + {f_1}_{Frontier} 关于Reverse 的对称字符就是Frontier - {f_1}_{Frontier} , 因为Reverse - {f_1}_{Reverse} + 1 < Frontier - {f_1}_{Frontier} + 1 , 所以字符Frontier - {f_1}_{Frontier} 也和前面提到的三个字符相同. 这样,s_{Frontier - {f_1}_{Frontier}} = s_{Frontier + {f_1}_{Frontier}} . 但是{f_1}_{Frontier} 则说明s_{Frontier - {f_1}_{Frontier}} \neq s_{Frontier + {f_1}_{Frontier}} , 相矛盾, 所以{f_1}_{Frontier} 不能更大. -
Reverse - {f_1}_{Reverse} + 1 > Frontier - {f_1}_{Frontier} + 1 根据已经求出的
f_{Reverse} 得,s_{Reverse - {f_1}_{Reverse}} \neq s_{Reverse + {f_1}_{Reverse}} . 又因为Reverse - {f_1}_{Reverse} \geq Frontier - {f_1}_{Frontier} + 1 , 所以对称的s_{2Frontier - Reverse + {f_1}_{Reverse}} \neq s_{2Frontier - Reverse - {f_1}_{Reverse}} , 所以这种情况{f_1}_i 也不会更大. -
Reverse - {f_1}_{Reverse} + 1 = Frontier - {f_1}_{Frontier} + 1 这时比较特殊, 因为
s_{Reverse - {f_1}_{Reverse}} \neq s_{Reverse + {f_1}_{Reverse}} ,s_{Frontier - {f_1}_{Frontier}} \neq s_{Frontier + {f_1}_{Frontier}} . 所以就算是s_{Frontier + {f_1}_{Frontier}} = s_{2i - Frontier - {f_1}_{Frontier}} = s_{Reverse + {f_1}_Reverse} 也无所谓, 这不会和任何已经求出的f_1 值冲突. 所以在{f_1}_i = Reverse - PalindromeL + 1 的基础上继续朴素即可.
对于求
时间复杂度
以
在大部分情况下, 每个
-
当
Frontier + {f_1}_{Frontier} - 1 < i 时一定有
Frontier + {f_1}_{Frontier} - 1 < i + {f_1}_i - 1 ,Frontier 被更新成i ,Frontier + {f_1}_{Frontier} 比原先多了至少{f_1}_i , 即本次操作步数. -
当
Frontier + {f_1}_ {Frontier} - 1 \leq i , 但Frontier + {f_1}_{Frontier} - 1 = Reverse + {f_1}_{Reverse} - 1 时.朴素从原来的
{f_1}_i 开始, 这时i + {f_1}_i - 1= Frontier + {f_1}_{Frontier} - 1 , 接下来每执行一步,i + {f_1}_{Frontier} 都增加1 .
因为每次朴素执行
综上, Manacher 可以在
模板
一个长度为
用
代码, 缺省源省略
unsigned n, Frontier(0), Ans(0), Tmp(0), f1[11000005], f2[11000005];
char a[11000005];
int main() {
fread(a+1,1,11000000,stdin); // fread 优化
n = strlen(a + 1); // 字符串长度
a[0] = 'A';
a[n + 1] = 'B'; // 哨兵
for (register unsigned i(1); i <= n; ++i) { // 先求 f1
if(i + 1 > Frontier + f1[Frontier]) { // 朴素
while (!(a[i - f1[i]] ^ a[i + f1[i]])) {
++f1[i];
}
Frontier = i; // 更新 Frontier
}
else {
register unsigned Reverse((Frontier << 1) - i), A(Reverse - f1[Reverse]), B(Frontier - f1[Frontier]);
f1[i] = Reverse - ((A < B) ? B : A); // 确定 f1[i] 下界
if (!(Reverse - f1[Reverse] ^ Frontier - f1[Frontier])) { // 特殊情况
while (!(a[i - f1[i]] ^ a[i + f1[i]])) {
++f1[i];
}
Frontier = i; // 更新 Frontier
}
}
Ans = ((Ans < f1[i]) ? f1[i] : Ans);
}
Ans = (Ans << 1) - 1; // 根据 max(f1) 求长度
Frontier = 0;
for (register unsigned i(1); i <= n; ++i) {
if(i + 1 > Frontier + f2[Frontier]) { // 朴素
while (!(a[i - f2[i] - 1] ^ a[i + f2[i]])) {
++f2[i];
}
Frontier = i; // 更新 Frontier
}
else {
register unsigned Reverse ((Frontier << 1) - i - 1), A(Reverse - f2[Reverse]), B(Frontier - f2[Frontier]);
f2[i] = Reverse - ((A < B) ? B : A); // 确定 f2[i] 下界
if (A == B) { // 特殊情况, 朴素
while (a[i - f2[i] - 1] == a[i + f2[i]]) {
++f2[i];
}
Frontier = i; // 更新 Frontier
}
}
Tmp = ((Tmp < f2[i]) ? f2[i] : Tmp);
}
Tmp <<= 1; // 根据 max(f2) 求长度
printf("%u\n", (Ans < Tmp) ? Tmp : Ans); // 奇偶取其大
return Wild_Donkey;
}