manacher小结
题目大意
n个字符组成的字符串,求最长回文串
$O$$($$n^3$$)$
枚举两端点,暴力往中间搜
$O$$($$n^2$$)$
枚举回文串终点,暴力往两边搜
$O$$($$n$$)$
$first:$
$j$与$i$关于$pos$对称,$S$为以$pos$为中间的回文串,$Maxright$为$S$的右端点,$s_1$为以$j$为中间的回文串
$s_2$为以$i$为中间的回文串
下面开始将$manacher$,要降低复杂度,就要减少重复的操作
$(1)$ $s_1$被$S$包含(且没到端点)
显然根据回文的性质$len_{s_1}$=$len_{s_2}$
$(2)$ $s_1$超过或触及端点
这时,我们只能确定,两条蓝线之间的部分(即不超过$Maxright$的部分)是回文的
于是从这个长度开始,从$i$的左右两边搜一遍,当左右字符不同,或者到达边界时停止
$(3)$ 当$i$在$Maxright$的右边
$s_2$还没有任何部分被访问过,只能从$i$的左右两边搜一遍,当左右字符不同,或者到达边界时停止
ps:记得要时刻更新$Maxright$和$pos$
My complete code:
#include<cstdio> #include<cstring> using namespace std; int n,ans; int hw[22000010]; char a[11000010],s[22000010]; inline int MIN(int g1,int g2){ return g1<=g2?g1:g2; } inline int MAX(int g1,int g2){ return g1>=g2?g1:g2; } inline void change(){ s[0]=s[1]='#'; for(int i=0;i<n;i++){ s[i*2+2]=a[i]; s[i*2+3]='#'; } n=n*2+2; s[n]=0; } inline void manacher(){ int maxright=0,mid=0; for(int i=1;i<n;i++){ if(i<maxright) hw[i]=MIN(hw[(mid<<1)-i],maxright-i); else hw[i]=1; while(s[i+hw[i]]==s[i-hw[i]]) ++hw[i]; if(hw[i]+i>maxright){ maxright=hw[i]+i; mid=i; } } } int main(){ scanf(" %s",a); n=strlen(a); change(); manacher(); for(int i=0;i<n;++i) ans=MAX(ans,hw[i]); printf("%d",ans-1); return 0; }