初学manacher

来源:互联网 发布:java调dll内存泄露 编辑:程序博客网 时间:2024/05/18 16:15

求最长回文子串的有力工具。

按照一般的暴力判断方法,我们可以枚举每一个中点,然后从这个位置暴力向两边拓展。

但是实际上我们可以重复利用已经求得的信息,使期望复杂度接近O(n)。

我们设 rad[ i ] 表示以i为中心的回文串的长度的一半(向下取整),那么这个回文串的右端点就是 i+rad[i],左端点就是 i-rad[i]。

为了避免分类讨论回文串长度的奇数偶数的情况,我们在每两个点之间都插入一个相同的字符(这个字符不能在原字符串出现)。

然后我们维护一个p、maxpos,表示当前算出来的所有回文串的右端点最大的那个回文串的中点和右端点。

以下来自轩神的博客

其中

红色:radi
橙色:radi−k
绿色:radi−k

radi−k<radi−k————————————————————————————————————————————————————————————

此时radi+k一定为radi−k否则根据对称性,radi可以更大。

radi−k>radi−k————————————————————————————————————————————————————————————

此时根据对称性也可以很显然地看出radi+k=radi−k

由①②有,当radi−k≠radi−k时,radi+k=min{radi−k,radi−k}

那么radi−k=radi+k时怎么办呢

radi−k=radi−k————————————————————————————————————————————————————————————

 

这时即使radi+k>radi−k也没有矛盾,此时应当令i+=k用朴素的算法扩大radi之后再用这个radi迭代更新。

hdu3068

//Serene#include<algorithm>#include<iostream>#include<cstring>#include<cstdlib>#include<cstdio>#include<cmath>using namespace std;const int maxn=110000+10;char s[2*maxn];int rad[2*maxn],len,maxpos,p,ans;int main() {while(scanf("%s",s)!=EOF) {len=strlen(s);maxpos=0;ans=0;for(int i=len;i>=0;--i) {s[(i+1)<<1]=s[i];s[i<<1|1]='#';}s[0]='#';for(int i=2;i<=2*len&&maxpos!=2*len+1;++i) {if(maxpos>i) rad[i]=min(maxpos-i,rad[2*p-i]);else rad[i]=0;while(i-rad[i]>0&&i+rad[i]<=2*len+1&&s[i-rad[i]]==s[i+rad[i]]) rad[i]++;rad[i]--;if(i+rad[i]>maxpos) p=i,maxpos=i+rad[i];ans=max(ans,rad[i]);}printf("%d\n",ans);}return 0;}/*abababbbabbbaabbaababaa*/


原创粉丝点击