manacher算法解析

manacher是很简单的字符串回文算法,作用是O(n)求出一个字符串的最长回文子串

下面给出这一算法的详解

首先,我们设原字符串是aaabba,很显然,这个字符串最长的回文子串长度为4

那么我们就要思考一种算法来计算出这个长度

于是manacher横空出世

首先,我们知道,一个回文子串一定有一个对称轴(或者你叫对称中心?),所以正常来讲,如果想O(n)求出最长回文子串,我们只需枚举每个对称轴,然后O(1)递推即可

可是现在就产生了一个问题:如果原字符串是上面说的那样,那...对称轴是在ab中间的啊,这怎么找出来啊

所以manacher需要一个操作,即在所有中间的位置插入一个'#'之类的无用字符,这样我们就可以忽略掉对称轴在两个字符中间的情况,只考虑对称轴落在字符上的情况即可

所以我们记p[i]表示以i为回文中心,回文半径为p[i]

然后我们考虑递推

怎么推?

我们记录一个maxp,表示在i之前所有的回文中心中,回文右端点最远在哪,记p0表示这个最远的右端点所对应的回文中心

 看蒙了?举个例子:

 

(图中黑线表示字符串,p0为回文中心,maxp为回文右端点)

接下来,我们考虑如何用这个东西维护p[i]

这是需要分类讨论的,即:

①:

若i<maxp,有:

p[i]=min(p[2*p0-i],p[p0]+p0-i);

这一点是显而易见的:如果i<maxp,那么我们显然可以找出一个j,使j与i关于p0对称,于是j=2*p0-i

而根据对称性,j周围的字符一定和i周围的字符相同,所以p[i]可以由p[j]来更新

如图所示,由于对称性,所以i右侧一部分和j左侧一部分相同,同时i左侧一部分和j右侧一部分相同,同时根据j的对称性,j左右两侧相同,故i左右两侧相同,为回文。

至于另一个也很好理解,就是从i到maxp的一段,假设他整个都能构成回文。这一值和刚才对称求出的一个值取最小值才能保证结果的合理性。

②:i>=maxp

没什么办法,暴力吧..

接下来谈谈暴力:

其实无论是情况①还是情况②都需要暴力,因为我们无法保证①求出的是最大值,我们只能保证①求出的是一个可能的合法答案

所以我们需要暴力从p[i]的已有值开始向下跳,跳到不能跳为止即可

剩下的就是代码了

比较好些

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
using namespace std;
char a[51000005];
char s[102000005];
int p[51000005];
int maxp,p0,l;
int ans=1;
void manacher()
{
	for(int i=1;i<l;i++)
	{
		if(i<maxp)
		{
			int j=(p0<<1)-i;
			p[i]=min(p[j],p[p0]+p0-i);
		}else
		{
			p[i]=1;
		}
		while(s[i-p[i]]==s[i+p[i]])
		{
			p[i]++;
		}
		if(i+p[i]>maxp)
		{
			p0=i;
			maxp=i+p[i];
		}
	}
}
int main()
{
	scanf("%s",a+1);
	l=strlen(a+1);
	p[1]=1;
	s[0]=s[1]='#';
	for(int i=1;i<=l;i++)
	{
		s[2*i]=a[i];
		s[2*i+1]='#';
	}
	l=(l<<1)+2;
	s[l]='0';
	manacher();
	for(int i=1;i<=l;i++)
	{
		ans=max(ans,p[i]-1);
	}
	printf("%d\n",ans);
	return 0;
}

 

posted @ 2018-09-18 21:07  lleozhang  Views(167)  Comments(0Edit  收藏  举报
levels of contents