manacher算法是一个在O(n)时间内求出一个字符串以每一个点为中心的回文串的长度的算法。
下面是它的流程:
首先我们在原串两个字符之间插入一个没有的字符(比如#),然后我们对这个新串进行操作,这样做的目的是为了保证求出来的回文串长度一定为奇数。
设r[i]表示已i为中心的回文串的半径。然后我们从前往后扫一遍这个串,一次求出r[i]。
在求r[i]时,我们要分类讨论。
设R表示之前最右的回文串的右端点,C表示最右回文串的中心,p1=i。
1、当R<p1时,我们直接以p1位中心枚举求r[i],求完之后更新一下R和C。
2、当R<=p1时
设p2表示p1关于C的对称点,L表示以C为中心的回文串的左端点,pl表示以p2位中心的回文串的左端点。
(1)L<pl,那么显然r[p1]=r[p2],这个画一下图就明白了。
(2)L>pl,那么r[p1]=R-p1+1,这个我们可以考虑反证:如果r[p1]还可以扩展,那么就可以推出r[C]也可以扩展。这个也可以画图理解。
(3)L=pl,那么设r[p1]的初值为R-p1+1,然后再暴力往两侧扩展。再这种情况下要更新R和C。
至此,r数组就求完了。因为R是不断往后移动的,所以算法的复杂度为O(n)。最后记得把r还原成原串,因为现在的串我们是加了#的。
贴一下代码:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define MAXN 220010
int a[MAXN],r[MAXN],n,ans;
char a1[MAXN];
int main()
{
int i,j,L,R,C,p1,p2,pl;
while(scanf("%s",a1)!=EOF)
{
n=strlen(a1);
for(i=1;i<=n*2+1;i++)
if(i%2==1)a[i]='#';
else a[i]=a1[i/2-1];
n=n*2+1;
R=0;C=0;ans=0;
for(i=1;i<=n;i++)
{
p1=i;
if(p1>R)
{
r[p1]=1;
while(p1-(r[p1]+1)+1>=1&&p1+(r[p1]+1)-1<=n&&a[p1-(r[p1]+1)+1]==a[p1+(r[p1]+1)-1])r[p1]++;
if(p1+r[p1]-1>R)R=p1+r[p1]-1,C=p1;
}
else
{
L=C-r[C]+1;p2=C-(p1-C);pl=p2-r[p2]+1;
if(L<pl)r[p1]=r[p2];
else if(L>pl)r[p1]=R-p1+1;
else
{
r[p1]=R-p1+1;
while(p1-(r[p1]+1)+1>=1&&p1+(r[p1]+1)-1<=n&&a[p1-(r[p1]+1)+1]==a[p1+(r[p1]+1)-1])r[p1]++;
if(p1+r[p1]-1>R)R=p1+r[p1]-1,C=p1;
}
}
if(a[i]=='#')
{
if(r[i]/2*2>ans)ans=r[i]/2*2;
}
else
if(r[i]%2==1)
{
if((r[i]/2+1)*2-1>ans)ans=(r[i]/2+1)*2-1;
}
else
{
if(r[i]-1>ans)ans=r[i]-1;
}
}
printf("%d\n",ans);
}
}