题意:
给定一个字符串a,找出字符串a中最长的回文子串,输出最长回文子串的长度。
题解:
manacher算法:求字符串最长回文子串长度的算法。详细解释可以看:hdu3068之manacher算法+详解。用图和文字解释的很清楚。
我在这里简要的解释一下:
回文分为两种:偶数长度和奇数长度。为了简便,我们可以将偶数长度的变成奇数长度的。怎么变,在各个字符之间加个不存在的符号,例如#。举例来说adda,可以变成#a#d#d#a#,即变为奇数的回文;同样的奇数长度的加#后还是奇数长度的最长回文。
接着我们定义dp[i]为以第i个字符为中心的回文半径。由于变成了奇数回文,所以回文半径=(回文长度+1)/2;又由于加了#,所以真实回文长度=改变后的回文半径-1。
我们枚举各个字符位置i,若存在一个i,使得i+dp[i]最大,我们用x=i,。然后分情况讨论:
1)i>x+dp[x]时,dp[i]=1,然后用while(a[i+dp[i]]==a[i-dp[i]])dp[i]++;向两边拓展。
2)i<=x+dp[x]时:由于x是回文,所以a[i]=a[2*i-x],之后就看dp[2*i-x]是否等于x+dp[x]-i了,也就是2*i-x的回文半径是否在x的回文半径内。若不相等,我们很容易证明dp[i]=min(dp[2*i-x],x+dp[x]-i);若相等则还需要用while向左右拓展。
在算法过程中,我们用maxv记录最长的回文半径即可。
注意:拓展的时候有可能越界,所以可以加越界判断,或者是两端字符加上不存在在的不同字符。
代码:
#include <cstdio> #include <cstring> #include <cmath> #include <cstdlib> #include <iostream> #include <algorithm> #include <queue> #include <map> #include <vector> using namespace std; const int maxn=11e4+10; char a[maxn*2]; int dp[maxn*2]; int Manacher(char *a,int *dp,int n)//求最大回文子串长度 { int i,j,k,x=0,maxv=0; for(i=2;i<2*n+1;i++) { if(x+dp[x]>i)dp[i]=min(dp[2*x-i],x+dp[x]-i); else dp[i]=1; while(a[i-dp[i]]==a[i+dp[i]])dp[i]++; if(x+dp[x]<i+dp[i])x=i; maxv=max(maxv,dp[i]); } return maxv-1; } int main() { while(scanf("%s",a)!=EOF) { int i,n; n=strlen(a); for(i=n;i>=0;i--) { a[2*i+2]=a[i]; a[2*i+1]='#'; } a[0]='*';//a[2*n+2]='\0',防止在manacher算法的while循环中溢出 printf("%d\n",Manacher(a,dp,n)); } }