您的位置:首页 > 其它

Manacher 模板 【bzoj2565】 最长双回文子串

2017-02-25 21:11 239 查看
Manacher是一种可以获得一个串以每一个位置为中心最长回文半径的算法。

它的主要思想就是利用已知的信息来减少获得新信息的不必要操作。



已知的信息就是已经求出的回文串,比如这张图,我们已知黑色部分是一个回文串。

我们要求以红色中点位置为中心的回文串。

假设我们红色部分就是答案,那么在黑色回文串的左边一定有一个跟红色部分一模一样的串,并且红色中点的答案就等于蓝色中点的答案,这样我们就可以快速获得答案。

但是如果黑色的右端在所求位置的左侧或者红色部分的答案有可能超出黑色右端点,这个时候需要暴力匹配。

我们每次都利用右端点最大的已知子串,这样我们只会不断地扩展右端点,所以相当于把这个串从左到右扫一遍,时间复杂度O(n)。

模板代码:

int mx=0,mi=0;
for(int i=1;i<=tot;i++)
{
if(mx>i) p[i]=Min(mx-i,p[2*mi-i]);
else r[i]=1;
while(a[i+p[i]]==a[i-p[i]]) p[i]++;
if(i+p[i]>mx) mi=i,mx=i+p[i];
}


题目大意:

求一个串的最长子串满足该子串可由两个回文串拼成。

题目分析:

插入分隔符,然后Manacher。

设置一个f数组,f[i]代表已第i个位置为结尾的回文串半径最长有多长。

这样可以通过枚举第二个串的回文中心,找到第一个串的结尾,再通过第一个串的结尾找到第一个串的回文中心,两个回文中心的距离恰好就是最长双回文子串的长度。

代码如下:

#include<cstdio>
#define N 120000
using namespace std;
inline int Max(int x,int y) { return x>y?x:y; }
inline int Min(int x,int y) { return x<y?x:y; }
char s
;
char a[N<<1];
int r[N<<1],tot;
int f[N<<1],ans;
int main()
{
scanf("%s",s);
for(int i=0;s[i];i++)
{
a[++tot]='#';
a[++tot]=s[i];
}
a[++tot]='#';
int mx=0,p=0;
for(int i=1;i<=tot;i++)
{
if(mx>i) r[i]=Min(mx-i,r[2*p-i]);
else r[i]=1;
if(!f[i]) f[i]=i;
while(a[i+r[i]]==a[i-r[i]])
{
if(!f[i+r[i]]) f[i+r[i]]=i;
r[i]++;
}
if(i+r[i]>mx) p=i,mx=i+r[i];
}
for(int i=1;i<=tot;i++) ans=Max(ans,i-f[i-r[i]]);
printf("%d\n",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Manacher