[Manacher]【学习笔记】
终于填坑啦......马拉车
课件上说的好短,但是明白了,讲解稍微修改一下抄上行了,比扩展KMP好写多了
求以每个字符为中心的最长回文串的半径。
如果要求可以以字符间隙为回文中心,就要在每两个字符之间及两端加入一个’#’,然后再解决。
令r[i]为以i为中心的最长回文半径。从左往右依次求r数组。
当前要求r[i],曾经的j+r[j]-1最大是p,对应的下标为a。如果r[2*a-i]+i-1<p,r[i]=r[2*a-i];否则r[i]≥p-i+1,暴力向后扩展。
2*a-i就是i关于a的对称位置,上面那句很显然啊<p的时候就被包括在a为中心的回文串里呀
实现上,直接r[i]=i<p?min(p-i+1,r[2*a-i]):1,然后往两边扩展就行了
模板题:HDU3068
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N=2e6+5; int n; char s[N],a[N]; int r[N]; void Manacher(char s[],int n){ int p=0,a,ans=0; for(int i=1;i<=n;i++){ r[i]=i<p?min(p-i+1,r[2*a-i]):1; while(s[i-r[i]]==s[i+r[i]]) r[i]++; if(i+r[i]-1>p) p=i+r[i]-1,a=i; ans=max(ans,r[i]); } printf("%d\n",ans-1); //for(int i=1;i<=n;i++) printf("%d ",r[i]);puts("\n"); } void iniStr(char s[]){ for(int i=1;i<=n;i++) a[(i<<1)-1]='#',a[i<<1]=s[i]; a[(n<<1)+1]='#'; a[0]='@';a[(n<<1)+2]='$'; } int main(){ freopen("in","r",stdin); while(scanf("%s",s+1)!=EOF){ n=strlen(s+1); iniStr(s); Manacher(a,n<<1|1); } }
Copyright:http://www.cnblogs.com/candy99/