Manacher算法

Manacher算法

作用

Manacher算法(又名“马拉车”算法)主要用于解决一类常用问题——最长回文子串(很多公司笔试都有这类题)。效率为O(n),线性的效率可以秒杀所有低能算法。


实现

首先关于回文串的长度有可能是奇数也有可能是偶数。通常情况下这个问题需要分类讨论,但是这真的很烦很烦。而Manacher算法对于这个细节问题采用了一种巧妙的方法:在每两个字符,以及字符的开始和结束部分都插入一个分隔字符(一定要在原串中未出现过,常用的比如’#’),使得回文串长度是奇数和偶数的情况统一起来。

接下来就是正式开始计算的步骤了,设p[i]=(以i为中心的最长回文子串长度+1)/2,也就是从i开始到以i为中心的最长回文子串的右边界之间的字符个数,这样定义的好处是:以i为中心的最长回文子串长度就是p[i]-1,那么如果p数组已求出,就可以在最后O(n)扫一趟max(p[i]-1)就是答案。那么现在问题就变为在O(n)的时间内求出p数组。

定义两个变量id,mx,其中mx是所有已求出来的所有最长回文子串的右边界中最远的右边界,而id是mx这个右边界对应的中心编号,即有mx=p[id]+id。那么就会出现一个神奇的结论:

如果mx>i,则p[i]>=min(p[2*id-i],mx-i);
这是啥?
好吧写详细点

j=2*id-i;     //也就是j是i关于id对应的那个字符
if (mx - i > P[j]) 
    P[i] = P[j];
else /* P[j] >= mx - i */
    P[i] = mx - i; // P[i] >= mx - i,取最小值,之后再匹配更新。

附图一张:
这里写图片描述

如图,在上面的情况就是mx-i>p[j]的情况,j的最长回文子串包含在id的最长回文子串内,由于 i 和 j 对称,以S[i]为中心的回文子串必然包含在以S[id]为中心的回文子串中,所以必有 P[i] = P[j]。
而如果p[j]>mx-i的时候,就是下面的情况,此时j的最长回文子串未完全包含在以id为中心的最长回文子串中,那么只有包含的白色部分满足这一性质,不包含的橙色部分因为j的左边橙色和i右边橙色可能不同,所以不能完全照搬j的回文子串,不过包含的白色部分的回文子串还是可以照搬的,然后由于你对i的右边还不了解,所以还是要一个一个的左右扩展(这就会为什么p[i]>=min(p[2*id-i],mx-i)是>=而不是=),不过和低效算法相比,这种算法已经利用已求出的回文子串减少了大量的冗余的计算,算是比较优秀的算法了。每次计算出p[i]后就比较i的回文子串右边界,如果比原有的远,更新mx和id。关于复杂度会比O(n)大一点,不过也很好了。

代码

模板提链接:hihocoder1032 最长回文子串

#include<cstdio>
#include<cstring>
#define maxn 2000005
using namespace std;
char s[maxn],st[maxn];
int p[maxn],tst,n,ans,id,mx;
int _min(int x,int y){return (x<y)?x:y;}
void manacher_get(){
    mx=ans=0;
    for (int i=1;i<=n;i++){
        if (mx>i) p[i]=_min(p[2*id-i],mx-i); else p[i]=1;
        while (i-p[i]>=1&&i+p[i]<=n&&s[i-p[i]]==s[i+p[i]]) p[i]++;  //防越界
        if (p[i]+i>mx) {id=i; mx=p[i]+i;}
    }
    for (int i=1;i<=n;i++)
      if (ans<p[i]-1) ans=p[i]-1;
}
int main()
{
    freopen("palid.in","r",stdin);
    freopen("palid.out","w",stdout);
    scanf("%d",&tst);
    while (tst--){
        scanf("%s",st+1);
        n=strlen(st+1); s[1]='#';
        for (int i=1;i<=n;i++){
            s[i*2]=st[i];
            s[i*2+1]='#';
        }
        n=2*n+1;
        manacher_get();
        printf("%d\n",ans);
    }
    return 0;
}

备注
由于作者水平极其有限,有错望各位神犇指出。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值