manacher算法

来源:互联网 发布:python unpack修饰符 编辑:程序博客网 时间:2024/05/29 05:52

一种很有趣的算法,为什么这么说呢,因为它的预处理很巧妙。

它通常是用来求一个字符串的回文串的长度的。下面介绍一下它的大致思路。(先介绍思路再说为什么会有这种思路)

1.特殊处理字符串,把字符串的所有字符用一个没有出现过的标识隔开,并特殊标识字符串的头:

aabb\0处理后就可以为$#a#a#b#b#\0aabaa\0处理后就可以为$#a#a#b#a#a#\0

for(int i=strlen(s);i>=0;i--){        s[i*2+2]=s[i];        s[i*2+1]='#';    }    s[0]='$';

2.设立变量maxn记录目前找寻的所有回文串中能涉及的最右端位置,设立变量id记录它所对应的对称轴的位置(大家会发现,经过处理的字符串只会存在奇数的回文串!),

用数组p[i]来记录中心为i的时候回文串长度。

int id=0,maxn=0;
3.从s[1]开始,顺序处理这个字符串(s[0]被标识成$了)

for(int i=1;i<=len*2+1;i++)

4.那么在这个循环内要干什么呢?首先当然要做的是判断当前你所判断的回文串是否完全在回文串id里面。

如何判断呢?我们还是举例吧

$#d#a#b#a#e#a#b#a#d#

大家看这个字符串我们会发现,这个字符串是关于e完全对称的

假设此时id是10,maxn是19(最长的回文串),i的位置是14。

那么有没有必要从新找一下i的回文串长度呢?是没有必要的,因为,我们可以轻易的发现p[id-(i-id)]是和p[i]对称的,因此p[id-(i-id)]的长度就是p[i]的长度了!

真的是如此吗?

看下面这个数据:

$#e#a#b#a#e#a#b#a#d#

p[id-(i-id)]是要比p[i]要大一位的,这是为什么呢?因为最右边的字母和最左边的字母是不匹配的啊。

还有另外一种情况是什么呢?是这样的  $#e#f#b#a#e#a#b#f#d# 此时很明显,p[id-(i-id)] 和p[i]二者相等


由此:

p[i]=min(p[id*2-i],maxn-i);
接下来还得考虑下面这种情况:

  $#a#a#b#a#e#a#b#a#e#a#b#

很明显此时p[i]的回文是很长的,我们在考虑完上述情况时还得考虑有没有可能p[i]是很大的并且大于p[id-(i-id)];


由此:

while(s[i-p[i]]==s[i+p[i]])p[i]++;

最后记得更新id 和maxn,完整代码如下:

#include <stdio.h>#include <cstring>#include <algorithm>using namespace std;#define MAX 100000char s[MAX*3];int p[MAX*3];int manacher(){    int ans=0,id=0,maxn=0,len=strlen(s);    for(int i=len;i>=0;i--){        s[i*2+2]=s[i];        s[i*2+1]='#';    }    s[0]='*';    for(int i=1;i<=len*2+1;i++){        if(maxn>i)p[i]=min(p[id*2-i],maxn-i);        else p[i]=1;        while(s[i-p[i]]==s[i+p[i]])p[i]++;        if(ans<p[i])ans=p[i];        if(maxn<p[i]+i){            maxn=p[i]+i;            id=i;        }    }    /*printf("%s\n ",s);    for(int i=1;i<=len*2+1;i++)printf("%d",p[i]);*/    return ans-1;}int main(){    memset(s,0,sizeof(s));    memset(p,0,sizeof(p));    scanf("%s",s);    int ans=manacher();    printf("%d\n",ans);}



0 0
原创粉丝点击