最长回文 HDU - 3068(求最长回文串的长度【马拉车算法Manacher】)

马拉车算法 Manacher‘s Algorithm 是用来查找一个字符串的最长回文子串的线性方法,由一个叫
Manacher 的人在 1975 年发明的,这个方法的最大贡献是在于将时间复杂度提升到了线性
dp[i] = ma > i ? min(dp[2 * mod - i], ma - i) : 1;
可以这么说,这行要是理解了,那么马拉车算法基本上就没啥问题了,那么这一行代码拆开来看就是
如果 ma > i, 则 p[i] = min( dp[2 *mod - i] , ma - i )
否则,dp[i] = 1(本身)
【care一】ma>i
(1)当 ma - i >dP[j] 的时候,以S[j]为中心的回文子串包含在以 S[mod] 为中心的回文子串中,由于 i 和 j 对称,以 S[i] 为中心的
回文子串必然包含在以 S[mod] 为中心的回文子串中,所以必有 P[i] = P[j],其中 j = 2*id - i,因为 j 到 id 之间到距离等于
id 到 i 之间到距离,为 i - id,所以 j = id - (i - id) = 2*id - i,参见下图。

  ma的对称点    =2*mod-i(i关于mod的对称点)   (mod为能延伸到最右端的位置的那个回文子串的中心点位置)
     mi         j              mod             i           ma(记录向右边延伸最多的位置)
—————|—————————|———————————————|———————————————|—————————|——|————————
                                                         |
         —————————————                  ———————|—————————|
                                               |  dp[j]  |(以dp[j]表示以v[j]字符为中心的回文子串的半径)
     —————————————————————————————————————————————————————————————
     
(2)当 dP[j] >= ma - i 的时候,以 S[j] 为中心的回文子串不一定完全包含于以 S[mod] 为中心的回文子串中,但是基于对称性可知,
下图中两个绿框所包围的部分是相同的,也就是说以 S[i] 为中心的回文子串,其向右至少会扩张到 ma的位置,也就是说 dP[i] = ma - i。
至于 ma 之后的部分是否对称,就只能老老实实去匹配了,这就是后面紧跟到 while 循环的作用。

  ma的对称点    =2*mod-i(i关于mod的对称点)   (mod为能延伸到最右端的位置的那个回文子串的中心点位置)
     mi         j              mod             i           ma(记录向右边延伸最多的位置)
—————|—————————|———————————————|———————————————|———————————|————————
                                                           |
     |——————————|—————————|         |——————————|———————————|——|
                                               |     dp[j] |  |(以dp[j]表示以v[j]字符为中心的回文子串的半径)
     |—————————————————————————————————————————————————————|
     
【care二】
对于 ma <= i 的情况,无法对 P[i] 做更多的假设,只能 P[i] = 1,然后再去匹配了。

题目:

给出一个只由小写英文字符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

AC代码

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int M=110010;
int dp[M*2];//dp[i]表示以v[i]字符为中心的回文子串的半径
char u[M],s[M*2];
int ans,len;
void manacher()
{/**马拉车算法的第一步是预处理,做法是在每一个字符的左右都加上一个特殊字符,比如加上 '#',这样做的好处是不论原字符串是奇数还是偶数个,处理之后得到的字符串的个数都是奇数个*/
    int l=0;
    s[l++]='$'; //s[0]='*',s[len*2+2]='\0',防止在while时dp[i]越界
    s[l++]='#';//Manacher处理回文串时,需要在每个字符串的中间以及两端插入不会出现的字符,然后再枚举
    for(int i=0; i<len; i++) //插入了len+1个'#',最终的s长度是1~(len+len+1)即2*len+1,首尾s[0]和s[2*len+2]要插入不同的字符
    {
        s[l++]=u[i];
        s[l++]='#';
    }
    s[l]='\0';
    int ma=0,mod=0;///ma记录向右边延伸最多的位置,
    for(int i=0; i<l; i++)
    {
        dp[i]=ma>i?min(dp[mod*2-i],ma-i):1;/**i为我们设定的回文串中心,若>ma,则此时没有回文串被包含,只能慢慢枚举,其他详细分析,见上*/
        while(s[dp[i]+i]==s[i-dp[i]]) dp[i]++;
        if(i+dp[i]>ma)
        {
            ma=i+dp[i];
            mod=i;
        }
    }
}
int main()
{
    while(~scanf("%s",u))
    {
        len=strlen(u);
        manacher();
        ans=-1;
        for(int i=0; i<2*len+2; i++)
            ans=max(ans,dp[i]-1);
        printf("%d\n",ans);
    }
    return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值