manacher算法总结

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);
}
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值