一个字符串中连续的一段就是这个字符串的子串,而回文串指的是12421这种从前往后读和从后往前读一模一样的字符串,所以最长回文子串的意思就是这个字符串中最长的为回文串的子串。很容易想到暴力的解法,一个一个枚举回文串的起始位置。
#include<cstdio> #include<cstring> #include<iostream> using namespace std; char str[1000010]; int main() { int n; cin>>n; while(n--) { memset(str,0,sizeof(str)); cin>>str; int ans; int i,j,k; bool flag; int len=strlen(str)-1; for(i=len;i>=0;i--) { for(j=0;j<=len;j++) { k=j+i; if(k<=len) { flag=1; int k1=k; int j1=j; while(k1>j1) { if(str[k1]!=str[j1]) flag=0; k1--; j1++; } if(flag) { ans=i+1; goto end; } } } } end:cout<<ans<<endl; } }在这个算法中存在大量重复的计算。如果一个字符串的[3, 7]这一段已经不是回文子串了,[2, 8]这一段就不可能是回文子串了。我们可以对这个算法进行改进,枚举回文串的中点,从中点向两边扩展。值得注意的是回文串有奇数长和偶数长两种情况,对于这两种情况我们要分别进行讨论。
#include<cstdio> #include<cstring> #include<iostream> using namespace std; char str[1000010]; int main() { int n; cin>>n; while(n--) { memset(str,0,sizeof(str)); cin>>str; int ans; int i,j,k,maxans=1; bool flag; int len=strlen(str)-1; //回文串的长度是奇数 for(i=0;i<=len;i++) { ans=1; j=i+1; k=i-1; while(j<=len&&k>=0) { if(str[k]==str[j]) { k--; j++; ans+=2; } else break; } if(ans>maxans) maxans=ans; } //回文串的长度是奇数 for(i=0;i<=len;i++) { j=i+1; if(str[i]==str[j]) { ans=2; j=j+1; k=i-1; while(j<=len&&k>=0) { if(str[k]==str[j]) { k--; j++; ans+=2; } else break; } if(ans>maxans) maxans=ans; } } cout<<maxans<<endl; } }下面该轮到我们要隆重介绍的Manacer算法登场了。前面我们说过枚举回文串的中点需要分情况讨论,如果在原来的字符串的前后以及每两个字符之间添加'#'字符(前提是这个'#'不在原串中出现过),显然现在所有的回文串的长度都是奇数的,这样就不用分情况讨论了,这是这个算法非常精妙的地方。我们还需要一个辅助数组p记录以每个字符为核心的最长回文字符串半径。p[i]最小为1,此时回文字符串就是字符串本身。 举个例子:
#include<cstdio> #include<cstring> #include<cstdlib> #include<iostream> #include<algorithm> using namespace std; const int MAX=1000010; char s[MAX]; char ss[MAX<<1]; int p[MAX<<1]; int solve(int len) { int ans=0; int right=-1; int id=-1; for(int i=0;i<len;i++) { int r=1; if(right>=i) r=max(r,min(right-i+1,p[2*id-i])); while((i-r+1>=0&&i+r-1<len)&&(ss[i-r+1]==ss[i+r-1])) r++; r--; if(i+r-1>right) { right=i+r-1; id=i; } p[i]=r; if(ans<r) ans=r; } return ans-1; } int main() { int Case=1; while(scanf("%s",s)!=EOF) { if(strcmp(s,"END")==0) break; int len=strlen(s); int cnt=0; for(int i=0;i<len;i++) { ss[cnt++]='#'; ss[cnt++]=s[i]; } ss[cnt++]='#'; printf("Case %d: %d\n",Case++,solve(cnt)); } }