manacher算法

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

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

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(第二个b的位置)。

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

真的是如此吗?

看下面这个数据:

$#e#d#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 100000
char 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值