• 最长回文子串(动规,中心扩散法,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]);
        }
    };
    
    时间复杂度分析

    参考

  • 相关阅读:
    Java学习8.17
    Java学习8.16
    Java学习8.15
    Java学习8.14
    Java学习8.13
    Java学习8.12
    Java学习8.11
    131. Palindrome Partitioning 回文串分割
    40. Combination Sum II 不允许使用重复元素
    39. Combination Sum 凑出一个和,可以重复用元素(含duplicates)
  • 原文地址:https://www.cnblogs.com/qbits/p/11229279.html
Copyright © 2020-2023  润新知