题目描述
给出一个只由小写英文字符a,b,c...y,z组成的字符串S,求S中最长回文串的长度.
字符串长度为n
输入输出格式
输入格式:一行小写英文字符a,b,c...y,z组成的字符串S
一个整数表示答案
题解:
说道回文串,相信大家都不陌生,毕竟我们在入门的时候解决过这种问题。
最原始的做法O(n^3):
枚举每个子串所用复杂度O(n^2),然后再用O(n)的时间判断这个子串是否为回文串,如果是就更新maxn。总时间复杂度O(n^3)。具体代码不再赘述。
稍微改进了一点点的做法O(n^2):
我们枚举字符串中每一个位置O(n),算出以它为对称轴的最长回文子串的
长度O(n)。总时间复杂度O(n^2)。具体代码也不再赘述。
正解manacher算法(中文名马拉车算法)O(n):
其实马拉车算法也没什么,只不过是在n平方算法的基础上做了一个小小的记忆化优化。我们使用n平方的算法的时候,你会发现,每次重复计算回文串左右端点会十分愚蠢。于是我们可以根据前面已经算出来的答案推出当前位置的答案。
首先大家得知道一个小技巧。我们在求回文串的时候,比如ababa,对称轴是在'a'这个字符上。而abba的对称轴是在两个'b'中间,因此我们把每个字符之间加上一个无关的字符,就能保证对称轴在字符的位置上了。
例如:abba我们可以变成 #a#b#b#a#。这样不影响对称轴的求解,同时也能确保对称轴在字符的位置上。
首先我们定义len[i]是以i为对称轴所能达到的回文串的最大半径,mx为当前已经计算好的最靠右的回文串的右端点,id为这个右端点对应的对称轴。
第一种情况:i<=mx
len[i]=len[j]
从p[i]=mx-i开始往后搜索。
第二种情况:i>mx
由于此时无法定位之前的len,所以只能暴力往后搜索。
AC代码:
#include<cstdio> #include<iostream> #include<cstring> using namespace std; const int Maxn=22000005; char s0[Maxn],s[Maxn]; int p[Maxn],n,mx,id,ans; inline int min(int x,int y){ return x<y?x:y; } inline int max(int x,int y){ return x>y?x:y; } int main(){ scanf("%s",s0+1); n=strlen(s0+1); for(int i=0;i<=2*n;i+=2){ s[i]='#'; s[i+1]=s0[i/2+1]; } n=n*2+1;s[0]='#'; for(int i=1;i<n;i++){ if(mx>i)p[i]=min(p[2*id-i],mx-i); else p[i]=1; while(i-p[i]>=0&&s[p[i]+i]==s[i-p[i]])p[i]++; if(p[i]+i>mx){ mx=p[i]+i; id=i; } ans=max(ans,p[i]-1); } printf("%d\n",ans); return 0; }