Manacher算法 学习笔记

Manacher算法

Manacher算法主要是用来解决字符串回文串的问题的。
Manacher算法把长度为奇数和偶数的字符串一起考虑

算法流程

假设我们现在要求出字符串 s s 的最长回文子串.
我们先对原串进行处理,把长度为奇数的和偶数的放在一起考虑。
其实就是在每两个字符串中间加入一个特殊的字符(只要是没有在原串中出现过的就可以,一般是#)

int Init()
{
    int len=strlen(s);
    int j=2;
    T[0]='$';
    T[1]='#';
    for(int i=0;i<len;i++)
    {
        T[j++]=s[i];
        T[j++]='#';
    }
    T[j]='\0';
    return j;
}

其中j是经过处理后的字符串的长度.
然后我们就可以开始对处理得到的字符串 T T 进行处理。
Manacher算法还要求一个辅助数Len[], Len[i] L e n [ i ] 表示以字符 T[i] T [ i ] 为中心的最长回文字串的最右字符到 T[i] T [ i ] 的长度,比如以 T[i] T [ i ] 为中心的最长回文字串是 T[l,r] T [ l , r ] ,那么 Len[i]=ri+1 L e n [ i ] = r − i + 1
显然对于 Len[i] L e n [ i ] ,我们有一个非常好的性质:
那就是 Len[i]1 L e n [ i ] − 1 就是以 i i 为中心的原串的最长回文子串的长度,(我们不是在原串中每隔两个字符又加了一个字符么)
既然Len[]有这么好的性质,那我们计算出 Len[] L e n [ ] 数组就可以求出最长回文子串的长度了。
怎样求 Len[] L e n [ ] 呢?
我们从左向右开始计算。
我们设 id i d 表示当前得到的最长回文子串的中心, mx m x 为当前得到的最长回文子串的右端点。
对于 Len L e n 数组的计算分为两种情况:
.当前要计算的 i i 点小于mx.
那么对于 i i 的最长回文子串只有两种情况:一种是i关于id的对称点 j j 的最长回文子串的长度,另一种是mxi的长度。
对于第一种情况:
这里写图片描述
显然因为 mxmx m x ‘ − m x 是以 id i d 为中心的最长回文子串,那么以 i i 为中心的最长回文子串的长度一定等于,i关于 id i d 的对称点 j j 的最长回文子串的长度。
对于第二种情况:
为什么还要min(p[j],mxi)呢?
这是因为 mx m x ‘ 左边和 mx m x 右边的字符是不一样的,有可能存在这样一种情况:
这里写图片描述
.当前要计算的 i i 点大于mx.
那么只能一位一位的计算了:
这里写图片描述

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
const int MAXN=11000300;
using namespace std;
int p[MAXN<<1];
char T[MAXN<<1],s[MAXN<<1];
int Init()
{
    int len=strlen(s);
    int j=2;
    T[0]='$';
    T[1]='#';
    for(int i=0;i<len;i++)
    {
        T[j++]=s[i];
        T[j++]='#';
    }
    T[j]='\0';
    return j;
}
int Manacher()
{
    int len=Init();
    int maxx=-1;
    int mx=0,id;
    for(int i=1;i<len;i++)
    {
        if(i<mx)
            p[i]=min(mx-i,p[2*id-i]);
        else
            p[i]=1;
        while(T[i+p[i]]==T[i-p[i]])
            p[i]++;
        if(i+p[i]>mx)
        {
            id=i;
            mx=i+p[i];
        }
        maxx=max(maxx,p[i]-1);
    }
    return maxx; 
}
int main()
{
    scanf("%s",s);
    printf("%d",Manacher());
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值