题目
就是给个字符串求其最长回文字串
字符串长度 len <= 11000000
分析
- 简单说一下 Manacher 吧
- 本来的暴力方法是对于每个点都从它自己往两边一直扩展直到不能回文为止,不过这样时间复杂度为
n^2
- 你会发现,其实有许多次扩展是多余的,想象一下,对于一串长一点的回文串,它对称的两边中,其中一边(比如说左半部分)若有回文串,那么另一边(右半部分)也肯定会有,按理来说就不用再讨论右边那个点了。
- Manacher 算法就是这样一个思路,先枚举
i
(从串首往后一位一位枚),同时记录下当前往右能扩展到最远的那一个回文串(中间那个点’Mid’和这个串最右边那个点’mx’),当然还要记录下以每个点为中心所能到达最长的回文串长’d[]’。 - 这样的话,每次讨论一个新的点 ‘i’ 时,要是它在当前 mx 那个串中,那就说明它的 d 值至少是它在
mx
这个串对称那个点的 d 值(还要判断一下要是取这个 d 值会不会超出 mx 这个串的范围,超出了要减一下),然后在这个的基础上再往外扩展。 - 这样一来,时间复杂度就变成了
n
,证明就是每一个点只会被当成“扩展出的那个点”一次。
程序
#include <cstdio>
#include <string>
#include <cstring>
#include <algorithm>
using namespace std;
int Mid,mx,k,i,j,n,d[22000005],ans;
char s[22000005],ch;
int main(){
for (s[n=1]='^',ch=getchar(); ch>='a' && ch<='z'; ch=getchar()) s[++n]=ch,s[++n]='$';
for (Mid=mx=0,i=1; i<=n; i++){
for (d[i]=(i<=mx?min(d[2*Mid-i],mx-i):0); s[i+d[i]+1]==s[i-d[i]-1]; d[i]++) //这一步尤为关键
if (i+d[i]>mx) Mid=i,mx=i+d[i];
}
for (i=1,mx=0; i<=n; i++) if(d[i]>mx) mx=d[i],Mid=i;
for (i=Mid-mx; i<=Mid+mx; i++) if (s[i]!='^' && s[i]!='$') ans++;
printf("%d",ans);
}