1. 程式人生 > >Manacher演算法,最長迴文串,時間複雜度O(n)

Manacher演算法,最長迴文串,時間複雜度O(n)

最長迴文子串 問題

對於一個字串,請設計一個高效演算法,計算其中最長迴文子串的長度。
給定字串A以及它的長度n,請返回最長迴文子串的長度。
測試樣例:
“abc1234321ab”,12
返回:7

中心擴充套件到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的左大的左側時

這裡寫圖片描述

i關於index對稱點i’在index的左大的右側時 有分為三種情況

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

這裡寫圖片描述

例項如下

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

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

這裡寫圖片描述

例項如下

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;
    }
};