最長回文子串(動規,中心擴散法,Manacher算法)


題目

leetcode:5. Longest Palindromic Substring

解法

動態規划

時間復雜度\(O(n^2)\),空間復雜度\(O(n^2)\)
基本解法直接看代碼

class Solution {
public:
    string longestPalindrome(string s) {
        int n = s.size();
        vector<vector<bool>> dp(n, vector<bool>(n, true));
        int rx, ry;
        
        rx = ry = 0;
        for(int l = 1; l < n; l++){
            for(int i = 0; i < n - l; i++){
                int j = i + l;
                if(s[i] == s[j] && (j - i < 3 || dp[i+1][j-1])){
                    dp[i][j] = true;
                    if(j - i > ry - rx){
                        ry = j;
                        rx = i;
                    }
                } else {
                    dp[i][j] = false;
                }
            }
        }
        
        return s.substr(rx, ry - rx + 1);
    }
};

中心擴散法

時間復雜度\(O(n^2)\),空間復雜度\(O(1)\)
我們先假定以某點為中心向兩端擴散,找到以該點為中心的最長回文子串

class Solution {
public:
    
    int rx, ry;
    void helper(string &s, int i, int offset){
        int left = i;
        int right = i + offset;
        while(left >= 0 && right < s.size() && s[left] == s[right]){
            left--;
            right++;
        }
        
        if(right - 1 - (left + 1) > ry - rx){
            ry = right - 1;
            rx = left + 1;
        } 
    }
    string longestPalindrome(string s) {
        int n = s.size();
    
        rx = ry = 0;
       
        for(int i = 0; i < n; i++){
            helper(s, i, 0);
            helper(s, i, 1);
        }
        
        return s.substr(rx, ry - rx + 1);
    }
};

Manacher算法

Manacher算法俗稱“馬拉車算法”,時間復雜度\(O(n)\),空間復雜度\(O(n)\)
因為回文字符串都有奇數長度的串和偶數長度的串,為了更好處理這兩種情況,可以在字符串中插入一特殊字符'#',使得新字符串長度全變為奇數長度,如"aa"變為"#a#a#",可以再字符串首部加入另一特殊字符'$'和尾部的'@',這樣就不用特殊處理越界問題(統一邊界處理)
以"122112321"為例經過上一步變成"@#1#2#2#1#1#2#3#2#1#"
Manacher算法使用一個輔助數組r[i]表示以t[i]為中心的最長回文子串的最右字符到t[i]的長度,如以t[i]為中心的最長回文子串為t[low, high],則r[i] = high - i + 1, t[low, high] = 2 * r[i]-1, len數組有一個性質,就是r[i]-1為該回文子串在原串中的長度,證明很簡單t[lowl, high]一定是以"#"開頭和結尾的,這樣插入的"#"是原來串中字符的兩倍還多一個,這樣原串中最長回文串的長度就為r[i]-1,這樣問題就轉為求最長的r[i]

計算len數組

算法主要利用了已有的回文子串的特點,減少了查找時間,從左往右計算len[i],同時保存一個之前計算最長回文子串的右端點的最大值R及對應的中心位置c,

  • i < R, 則先找i關於c對稱點j=2*c-i,則至少r[i] \(\geq\) min(R - i + 1, p[j]), 超出部分再手工匹配
  • i >= R, 則不能利用以后的知識做任何假設,只能假定其至少為1,再手工匹配
class Solution {
public:
  
    string longestPalindrome(string s) {
        int n = s.size();
        if(n == 0) return "";
        string ns;
        ns.push_back('$');
        for(int i = 0; i < n; i++){
            ns.push_back('#');
            ns.push_back(s[i]);
        }
        ns.push_back('#');
        ns.push_back('@');
        n = ns.size();
        vector<int> r(n);
        int c, R, C, MAX;
        R = -1;
        MAX = 0;
        C = 0;
        for(int i = 1; i < n; i++){
            r[i] = i < R ? min(r[2 * c - i], R - i + 1) : 1;
            while(ns[i + r[i]] == ns[i - r[i]]) r[i]++;
            r[i]--;
            if(i + r[i] > R){
                R = i + r[i];
                c = i;
            }
            if(r[i] > MAX){
                MAX = r[i];
                C = i;
            }
           
        }
        
        return s.substr((C-MAX)/2, r[C]);
    }
};
時間復雜度分析

參考


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM