最长回文(Manacher 马拉车算法模板解析)

给出一个只由小写英文字符a,b,c...y,z组成的字符串S,求S中最长回文串的长度. 
回文就是正反读都是一样的字符串,如aba, abba等

Input

输入有多组case,不超过120组,每组输入为一行小写英文字符a,b,c...y,z组成的字符串S 
两组case之间由空行隔开(该空行不用处理) 
字符串长度len <= 110000

Output

每一行一个整数x,对应一组case,表示该组case的字符串中所包含的最长回文长度. 

Sample Input

aaaa

abab

Sample Output

4
3

题意:求一个字符串的最长回文串。

思路:马拉车算法模板。

马拉车算法:对于任何一个回文串,可能是奇数个(aba),也可能是偶数个(abba)。对于奇数特别好处理,枚举每一个字符为中点,两边找即可。对于偶数个的话就比较麻烦了。为了简化问题,我们把字符串都转化一下,每个字符前后前面都加一个无关的字符.

eg:   aba    -->  #a#b#a# (新串为奇数个)

        abba   --> #a#b#b#a#(新串为奇数个)

其实原串后面还有一个  '\0'   为了简化问题(不特判结尾)我们在最开始再加一个无关字符 $  

eg:    aba    -->  $#a#b#a#0

         abba   --> $#a#b#b#a#0

然后我们就来找回文串了,

我们定义Mp[ i ] 是以 i 为中心的回文串的半径 (Mp[i] = 1 代表这个回文串只有自己)。

id 是当前已知最长回文串的中点,  mx 是当前已知最长回文串的右端点

对于当前位置 i 它的回文半径怎么算呢?

因为我们是顺序遍历的,所以到达 i 之前的所有 Mp[ i ]都已经得到了。所有我们可以利用 i ,j 关于ID对称的特性来减少“暴力”求解的次数(当然是直求长度了)。

当 mx  > i 时

      ①.  mx - i > p[ j ] :因为上图蓝色部分已经是回文串了,也就是 i 的最小半径应该和 j 的半径相等。(Mp[ i ] >= Mp[ j ])

                  所以最小半径为 p [ j ]

      ②.   mx - i < p[ j ] :黄色部分的长度小于 p [ j ] 那黄色部分再往右就不知道了, 所以最小半径为 mx - i

当 mx <= i 时,就没能利用 对称点 j 了,所以最小半径为 1

接下来就是暴力循环扩增 Mp[ i ]了

最后只需要求Mp的最大值即可,虽然Mp记录的半径,但由于字符串扩增了,所有Mp就是答案。

代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 110010
using namespace std;
char Ma[N<<1],s[N];
int Mp[N<<1];
void Manacher(int len)
{
    int l=0;
    Ma[l++]='$';
    Ma[l++]='#';
    for(int i=0; i<len; i++)
    {
        Ma[l++]=s[i];
        Ma[l++]='#';
    }
    Ma[l]=0;
    int mx=0,id=0;
    for(int i=0; i<l; i++)
    {
        Mp[i]=mx>i?min(Mp[2*id-i],mx-i):1;
        while(Ma[i+Mp[i]]==Ma[i-Mp[i]])Mp[i]++;
        if(i+Mp[i]>mx)
        {
            mx=i+Mp[i];
            id=i;
        }
    }
}
int main()
{
    while(~scanf("%s",s))
    {
        int len=strlen(s);
        Manacher(len);
        int ans=0;
        for(int i=0; i<2*len+2; i++)
            ans=max(ans,Mp[i]-1);
        printf("%d\n",ans);
    }
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值