YunYan

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

关于Macacher算法,以前只知道一个模板,现在在理解的基础上记录一下它的思路

对于求字符串的回文子串的常见方法有:

1 暴力求解,即枚举中点,然后向左右扩散。

2 可以将字符串反转,然后通过dp求两个字符串的最长公共子序列。

这两种方法的时间复杂度都是O(n^2)。

Manacher算法充分用到了回文串的完全对称性,可以在O(n)的时间复杂度内求处字符串的最长回文子串。

回文串大致可以分为两种,奇回文和偶回文, 为了方便处理,马拉车算法将这两种回文串变为一种后再进行处理。如何变为一种呢?

例如:

 abcdcba

 #a#b#c#d#c#b#a#

在每个字符前后都加上#(不一定非要#,只要是没出现过的字符就可以了)

这样无论原来字符串是奇数还是偶数,新的字符串都是奇数,假如说之前存在回文串abba,加上#后就会变成#a#b#b#a#,长度为2x+1所以,新的字符串出现的回文串一定是奇数的。

然后我们定义一个数组p[i],在新的字符串中,p[i]表示以i为中心的最大回文半径(包括i)。

i       0 1 2 3 4 5 6 7 8 9 10 11 12 13
arr[i]  $ # c # a # b # b # a  #  f  #
p[i]      1 2 1 2 1 2 5 2 1 2  1  2  1
我们可以发现以i为中心的回文串的长度为(2*p[i]-1-1)/2,减去第一个1是p[i]乘以2后,多算了一个i,减去第二个1是减去最后一个位置的#,然后除以2,就是回文串的实际长度了,
也就是p[i]-1,然后就是求p数组,另外定义mx和id,表示从1~i的回文串可到达的最右端,id是该回文串中心位置。
核心部分:p[i] = mx > i ? min(p[2*id-i], mx-i) : 1;

 

当i>mx的时候,p[i]=1;

当i<=mx的时候,可能会出现上述两种情况,j是i关于id的对称点.2*id-i。那么p[i]可完全由p[j]来决定,第一种情况就是以i为中新的回文串包含在以id为中心的回文串内部,

因此,p[i]=p[j],第二种情况是p[j]太大了,使得j的左边界超过了与mx对称的左边界,同里p[i]可能会超过mx,为什么是可能呢?因为mx右边和与mx对陈的左边字符不一样,因此p[i]我们只能取mx-i。

所以p[i]=min(p[j],mx-i)

code:

 

void countSubstrings(string s) {
        string t = "$#";
        for(int i=0; i<s.size(); i++){
            t+=s[i];
            t+="#";
        } 
        vector<int> p(t.size() , 0); 
        int ans=0;
        int mx = 0, id = 0, reCenter = 0, reLen = 0;
        for(int i=1; i<t.size(); i++){
            p[i] = mx > i ? min(mx - i , p[2*id - i]) : 1;
            while(t[i + p[i]] == t[i - p[i]]) p[i]++;
            if(i+p[i] > mx){
                mx = i+p[i];
                id = i;
            }
        } 
    }

 

 

 

 




posted on 2020-08-29 18:24  Target--fly  阅读(110)  评论(0)    收藏  举报