[Manacher]【学习笔记】

终于填坑啦......马拉车


 

课件上说的好短,但是明白了,讲解稍微修改一下抄上行了,比扩展KMP好写多了

求以每个字符为中心的最长回文串的半径。
如果要求可以以字符间隙为回文中心,就要在每两个字符之间及两端加入一个’#’,然后再解决。
令r[i]为以i为中心的最长回文半径。从左往右依次求r数组。
当前要求r[i],曾经的j+r[j]-1最大是p,对应的下标为a。如果r[2*a-i]+i-1<p,r[i]=r[2*a-i];否则r[i]≥p-i+1,暴力向后扩展。

2*a-i就是i关于a的对称位置,上面那句很显然啊<p的时候就被包括在a为中心的回文串里呀

实现上,直接r[i]=i<p?min(p-i+1,r[2*a-i]):1,然后往两边扩展就行了

 

模板题:HDU3068

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=2e6+5;
int n;
char s[N],a[N];
int r[N];
void Manacher(char s[],int n){
    int p=0,a,ans=0;
    for(int i=1;i<=n;i++){
        r[i]=i<p?min(p-i+1,r[2*a-i]):1;
        while(s[i-r[i]]==s[i+r[i]]) r[i]++;
        if(i+r[i]-1>p) p=i+r[i]-1,a=i;
        ans=max(ans,r[i]);
    }
    printf("%d\n",ans-1);
    //for(int i=1;i<=n;i++) printf("%d ",r[i]);puts("\n");
}
void iniStr(char s[]){
    for(int i=1;i<=n;i++)
        a[(i<<1)-1]='#',a[i<<1]=s[i];
    a[(n<<1)+1]='#';
    a[0]='@';a[(n<<1)+2]='$';
}
int main(){
    freopen("in","r",stdin);
    while(scanf("%s",s+1)!=EOF){
        n=strlen(s+1);
        iniStr(s);
        Manacher(a,n<<1|1);
    }
}

 

posted @ 2017-02-13 20:44  Candy?  阅读(272)  评论(0编辑  收藏  举报