码迷,mamicode.com
首页 > 其他好文 > 详细

Manacher 学习

时间:2018-07-25 11:28:52      阅读:214      评论:0      收藏:0      [点我收藏+]

标签:lse   相同   现在   fill   蓝色   text   lan   i++   分享   

推荐博客 :https://blog.csdn.net/zzkksunboy/article/details/72600679

作用

线性时间解决最长回文子串问题。

思想

Manacher充分利用了回文的性质,从而达到线性时间。


首先先加一个小优化,就是在每两个字符(包括头尾)之间加没出现的字符(如#),这样所有字符串长度就都是奇数了,方便了很多。
abcde->#a#b#c#d#e#


记录p[i]表示i能向两边推(包括i)的最大距离,如果能求出p,则答案就是max(p)-1了(以i为中点的最长回文为2*p[i]-1,但这是加过字符后的答案,把加进去的字符干掉,最长回文就是p[i]-1)。

我们假设p[1~i-1]已经求好了,现在要求p[i]:

假设当前能达到的最右边为R,对应的中点为pos,j是i的对称点。

1.当i<R时
技术分享图片
由于L~R是回文,所以p[i]=p[j](i的最长回文和j的最长回文相同)。
技术分享图片
这种情况是另一种:j的最长回文跳出L了。那么i的最长回文就不一定是j的最长回文了,但蓝色的肯定还是满足的。

综上所述,p[i]=min(p[2*pos-i],R-i)。
2.当i>=R时
由于后面是未知的,于是只能暴力处理了。

 

效率

复杂度是 O(n)的

因为R不会减小,每次暴力处理的时候,p[i]增大多少,就说明R增大多少,而R最多增加len次。

核心代码:

void Manacher() {
    for(int i = 1; i <= len; i++){
        now[2*i-1] = ‘#‘;
        now[2*i] = s[i];
    }
    now[2*len+1] = ‘#‘;
    len = len*2+1;
    
    int mx = 0, id = 0;
    for(int i = 1; i <= len; i++){
        if (i <= mx) p[i] = min(p[2*id-i], mx-i); else p[i] = 1;
        while(i-p[i]>=1 && i+p[i]<=len && now[i-p[i]]==now[i+p[i]]) p[i]++;
        
        if (i+p[i] > mx){mx = i+p[i], id = i;}
        ans = max(ans, p[i]); 
    }
}

 

Manacher 学习

标签:lse   相同   现在   fill   蓝色   text   lan   i++   分享   

原文地址:https://www.cnblogs.com/ccut-ry/p/9364613.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!