Manacher算法,最长回文串,时间复杂度O(n)

本文地址:http://blog.csdn.net/qq_26437925/article/details/52181738
本文参考了牛客网左程云牛客网视频,对Manacher算法整理成文


最长回文子串 问题

对于一个字符串,请设计一个高效算法,计算其中最长回文子串的长度。
给定字符串A以及它的长度n,请返回最长回文子串的长度。
测试样例:
“abc1234321ab”,12
返回:7

http://www.nowcoder.com/practice/b4525d1d84934cf280439aeecc36f4af?tpId=49&tqId=29360&rp=5&ru=/ta/2016test&qru=/ta/2016test/question-ranking


中心扩展到Manacher

中心扩展时 奇数和偶数的情况是不一样的,需要分开处理
采用Manacher 算法,对字符串每隔一个位置加上一个特殊字符特殊字符随便是什么字符),这样就转化成为奇数求解了
例如
“121” 变成 “#1#2#1#”
“1221” 变成 “#1#2#2#1#”

接下来将逐步求解

所需变量

p[] int型数组

* p[i] 表示 以i位置为中心的回文半径长(其长度减去1,表示在原字符串中以i为中心的回文长度)

pR int型变量

* 表示 回文半径最长的位置的下一个位置(pR总是比上一个pR值要大的)

index 型变量

* 表示 回文中心点,pR更新时index要跟着更新,否则index不用更新

初始情况
当i = 0时, p[0] = 1, index = 0, pR =1
到i = 1时 p[1] = 2 , index = 1, pR = 3
接着可以从i=2开始计算


共4种情况(如下的1,2,3,4)

说明
* 左大为关于某个点最大回文串的左位置
* 右大为关于某个点最大回文串的左位置
* i为当前遍历的位置
* i’为i关于index的对称位置

1. i关于index对称点i’在index的左大的左侧时

Manacher算法,最长回文串,时间复杂度O(n)_第1张图片


i关于index对称点i’在index的左大的右侧时 有分为三种情况

2. i’的左大 大于 index的左大

Manacher算法,最长回文串,时间复杂度O(n)_第2张图片

实例如下


3. i’的左大 小于 index的左大


4. i’的左大 等于 index的左大

Manacher算法,最长回文串,时间复杂度O(n)_第3张图片

实例如下



ac代码,按照上文思路即可

class Palindrome {
public:
    int getLongestPalindrome(string A, int n) {
        // write code here
        if (n <= 0)
            return 0;

        string str;//添加n+1个特殊字符',回文串都会变成奇数
        for (int i = 0; i < n; i++)
        {
            str += "#";
            str += A[i];
        }
        str += "#";

        int len = n + n + 1;
        vector<int> p(len); // p[i]为回文中心半径数组(包括以str[i])
        int index; // 回文中心位置, pR更新时 index跟着跟新; pR不更新,index保持不变
        int pR; // 回文中心最右边的 下一个

        int maxlen = 1; //最大回文长度变量

        // i=0, i=1单独处理
        p[0] = 1;
        p[1] = 2;
        index = 1;
        pR = 3;

        // 从 p[2] 开始扫描
        for (int i = 2; i < len; i++)
        {
            int _i = index - (i - index);   //i的对称位置i'
            int index_left = index - p[index] + 1;  // index的左大
            int index_right = pR -1;    // index的右大
            int _i_left = _i -p[_i] + 1;    //i的对称位置i'的左大

            if (_i < index_left)
            {
                int j = i + 1;
                int _j = i - 1;
                while (str[_j] == str[j] && _j >= 0 && j < len)
                {
                    _j--;
                    j++;
                    if (_j < 0 || j >= len)
                        break;
                }
                p[i] = j - i;
                pR = j; // 更新pR 和 index
                index = i;
            }
            else{

                if (index_left < _i_left)
                {
                    p[i] = p[_i];
                }
                else if (index_left > _i_left){
                    p[i] = index_right - i + 1;
                }
                else//if (_i_left == index_left)
                {
                    // 往外扩
                    int j = pR;
                    int _j = i - (pR - i);
                    while (str[_j] == str[j] && _j >= 0 && j < len)
                    {
                        _j--;
                        j++;
                        if (_j < 0 || j >= len)
                            break;
                    }
                    p[i] = j - i;
                    pR = j; // 更新pR 和 index
                    index = i;
                }
            }

            if (p[i] - 1 > maxlen)
                maxlen = p[i] - 1;
        }// for
        return maxlen;
    }
};

Longest Palindromic Substring (leetcode)

ac代码

class Solution {
public:
    string longestPalindrome(string s) {
        int n = s.size();
        if (n <= 1)
            return s;

        // 添加特殊字符
        string str;
        for (int i = 0; i < n; i++){
            str += "#";
            str += s[i];
        }
        str += "#";

        int len = 2 * n + 1;
        vector<int> p(len); // p[i]为回文中心半径数组(包括以str[i])
        int index; // 回文中心位置, pR更新时 index跟着跟新; pR不更新,index保持不变
        int pR; // // 回文中心最右边的 下一个

        int maxlen = 1; //最大回文长度变量
        int pos; // 记录最大回文的中心位置

        // i=0, i=1单独处理
        p[0] = 1;
        p[1] = 2;
        index = 1;
        pR = 3;
        pos = 1;

        for (int i = 2; i < len; i++)
        {
            int _i = index - (i - index);
            int index_left = index - p[index] + 1;
            int index_right = pR - 1;
            int _i_left = _i - p[_i] + 1;

            if (_i_left < index_left) //  1
            {
                int j = i + 1;
                int _j = i - 1;
                while (str[j] == str[_j] && _j >= 0 && j < len)
                {
                    _j--;
                    j++;
                    if (_j < 0 || j >= len)
                        break;
                }
                p[i] = j - i;
                pR = j;
                index = i;
            }
            else{
                if (index_left < _i_left){ // 2 
                    p[i] = p[_i];
                }
                else if (index_left > _i_left){ // 3
                    p[i] = index_right - i - 1;
                }
                else//if (_i_left == index_left) // 4
                {
                    // 往外扩
                    int j = pR;
                    int _j = i - (pR - i);
                    while (str[_j] == str[j] && _j >= 0 && j < len)
                    {
                        _j--;
                        j++;
                        if (_j < 0 || j >= len)
                            break;
                    }
                    p[i] = j - i;
                    pR = j; // 更新pR 和 index
                    index = i;
                }
            }

            if (p[i] - 1 > maxlen){
                maxlen = p[i] - 1;
                pos = i;
            }

        }// end for

        // 得到子串
        int realpos = pos / 2;
        int startpos = realpos - maxlen / 2;
        string ans = s.substr(startpos, maxlen);

        return ans;
    }
};

你可能感兴趣的:(ACM--字符串处理)