马拉车(manacher)算法可以在O(n)时间内求出最长回文子串或回文子串的个数。
马拉车一开始还是比较晦涩难懂的,这里推荐两篇文章,可以帮助了解马拉车算法的思想:
第一篇使用了漫画讲解,相对第二篇文章更容易理解马拉车算法的思想,但代码实现上不如第二篇简洁。故推荐先阅读第一篇,要实现代码时再参照第二篇。
#include<algorithm>
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int maxn = 3e5 + 5;
char s[maxn], t[maxn];
int p[maxn];
int main(){
while(~scanf("%s", s)){
int l = strlen(s), len = 0;
t[len++] = '$'; // 在字符串开头和结尾都添加一个特殊字符(如'$', 注意不要相同),使后续计算可以避免判断越界
t[len++] = '#';
for(int i = 0; i < l; ++i){
t[len++] = s[i];
t[len++] = '#';
}
t[len] = '&';
int center = 0, max_right = 0;
for(int i = 1; i < len; ++i){
int i_mirror = 2 * center - i;
if(max_right > i){ // 在最右边界的覆盖范围内, 就利用回文串特征计算长度
p[i] = min(max_right - i, p[i_mirror]); // 使用min()是为了防止超出最右边界
}
else {
p[i] = 0;
}
while(t[i - 1 - p[i]] == t[i + 1 + p[i]])
++p[i];
if(i + p[i] > max_right){ // 更新右边界的回文串中心
center = i;
max_right = i + p[i];
}
}
int max_len = 0;
// int pos = 0; //记录最长回文子串中心的位置
for(int i = 1; i < len; ++i)
if(max_len < p[i]){
max_len = p[i];
// pos = i;
}
printf("%d\n", max_len); //最长回文串的起始位置为: (pos - max_len) / 2
}
return 0;
}