马拉车算法总结

【算法简述】

马拉车(Manacher)算法是在O(n)时间内解决寻找源字符串的最长回文子串S的问题的算法。

朴素算法情况下对于每一个S[i]都要左右遍历其最大回文子串,所以时间复杂度是O(n2

 

【算法原理】

充分利用之前求得的S【j】,为求S【i】服务。

预处理:在每个字符左右两边插入#将字符串变成奇数串

算法核心:

p[i] = mx > i ? min(p[2 * id - i], mx - i) : 1;

其中p[i]为S[i]的回文半径,mx为i之前某一回文串右侧最远延伸到的位置,id为该回文串的中心点位置,很显然从mx的对称点到mx这一段是关于id对称的。

如何利用之前求得的s[j]来求s[i]的回文串长度呢?
分情况讨论
(1) 当 i在mx左侧,p[j] < mx - i,即S[j]的回文子串的左端点不会超过mx的对称点,那么很显然S[i]的回文子串的右端点不会超过mx,所以此时取P[i] = p[j]

 

(2)当i在mx左侧, p[j] > mx - i时,即S[j]的回文子串的左端点超过mx的对称点,那么很显然S[i]的回文子串的右端点至少要到mx,至于mx右侧的,只能一个一个匹配以确定p[i]了。

(3)当i在mx右侧,即i > mx,初始化p[i] = 1.这时候很显然,mx和id是要更新的。因为此时回文串右侧延伸的位置已经超过mx了。

 

【模板】

 

    //返回源字符串S的最长回文子串 
    string Manacher(string s){
        //预处理源串 
        string t = "#";
        for(int i=0; i<s.size(); i++){
            t+=s[i];
            t+="#";
        }
        //新建p数组,大小和t串一致,初始化为0 ,p[i]表示以t[i]为中心的回文串半径 
        vector<int> p(t.size() , 0); 
        //设定重要参数 mx(某回文串延伸到的最右边下标),id(mx所属回文串中心下标),
        //reCenter(结果最大回文串中心下标),reLen(最大长回文长度) 
        int mx = 0, id = 0, reCenter = 0, reLen = 0;
        //遍历t字符串
        for(int i=1; i<t.size(); i++){
            //核心算法 
            p[i] = mx > i ? min(mx - i , p[2*id - i]) : 1;
            //上面的语句只能确定i~mx的回文情况,至于mx之后的部分是否对称,就只能老老实实去匹配了,匹配一个p[i]++ 
             while (i-p[i] >=0 && i+p[i]<t.size() && t[i + p[i]] == t[i - p[i]]) p[i]++;
            //当t[i]匹配的 右边界超过mx时mx和id就更新 
            if(i+p[i] > mx){
                mx = i+p[i];
                id = i;
            }
            //更新结果数据 
            if(p[i] > reLen){
                reLen = p[i];
                reCenter = i;    
            }
        }
        reLen = (reLen*2 - 1) / 2;
        int st = reCenter/2 - reLen / 2;
        return s.substr(st , reLen)  ;
        
    }

 

posted @ 2018-09-26 10:16  西风show码  阅读(2820)  评论(2编辑  收藏  举报