5. 最长回文子串

395 阅读1分钟
  1. 最长回文子串:找到最长的回文子串

动态规划一:

class Solution {
public:
    string longestPalindrome(string s) {
       
        int n = s.size();
        vector<vector<int>> dp(n, vector<int>(n));
        string result;
        // delta间距或者跨度
        for (int delta = 0;delta < n;++delta) {
            for (int i = 0;i + delta < n;++i) {
                
                int j = i + delta;
                if (delta == 0) {
                    dp[i][j] = true;
                } else if (delta == 1) {
                    dp[i][j] = s[i] == s[j];
                } else {
                    dp[i][j] = s[i] == s[j] && dp[i+1][j-1];
                }
                // 原回文串长度小于跨度则更新串
                if (dp[i][j] && result.size() < delta + 1) {
                    result = s.substr(i, j + 1);
                }
            }
        }
        return result;
    }
};

时间复杂度:O(n^2);空间复杂度:O(n^2);

方法二:回文中心往外扩展

class Solution {
public:
    string longestPalindrome(string s) {
       
        int n = s.size();
        int start = 0, end = 0;
        for (int i = 0;i < n; ++i) {
            // 从中间开始
            auto [l1, r1]  = expandStr(s, i, i);
            // 从中间俩个相等字符开始
            auto [l2, r2] = expandStr(s, i, i+1);
            if (r1 - l1 > end - start) {
                start = l1;
                end = r1;
            } 
            if (r2 - l2 > end - start) {
                start = l2;
                end = r2;
            } 
        }
        return s.substr(start, end - start + 1);
    }
    pair<int, int> expandStr(string s, int left, int right) {
        int n = s.size();
        while(--left >= 0 && ++right <= n) {
            if (s[left] != s[right]) {
                break;
            }
        }
        return {left + 1, right - 1};
        
    }
};

方法三:Manacher算法:填充字符串变为奇数位

class Solution {
public:
    int expand(const string& s, int left, int right) {
        while (left >= 0 && right < s.size() && s[left] == s[right]) {
            --left;
            ++right;
        }
        return (right - left - 2) / 2;
    }

    string longestPalindrome(string s) {
        int start = 0, end = -1;
        string t = "#";
        for (char c: s) {
            t += c;
            t += '#';
        }
        t += '#';
        s = t;

        vector<int> arm_len;
        int right = -1, j = -1;
        for (int i = 0; i < s.size(); ++i) {
            int cur_arm_len;
            if (right >= i) {
                // i关于j对称点
                int i_sym = j * 2 - i;
                // right大于i时,可从臂长缓存获取对称点臂长,和剩余右端长度取最小值
                // 获取i的臂长
                int min_arm_len = min(arm_len[i_sym], right - i);
                // 从i开始进行臂长范围外扩展
                cur_arm_len = expand(s, i - min_arm_len, i + min_arm_len);
            } else {
                // 从i奇数扩展 获取臂长
                cur_arm_len = expand(s, i, i);
            }
            // 位置点i所对应臂长
            arm_len.push_back(cur_arm_len);
            if (i + cur_arm_len > right) {
                j = i;
                right = i + cur_arm_len;
            }
            if (cur_arm_len * 2 + 1 > end - start) {
                start = i - cur_arm_len;
                end = i + cur_arm_len;
            }
        }

        string ans;
        for (int i = start; i <= end; ++i) {
            if (s[i] != '#') {
                ans += s[i];
            }
        }
        return ans;
    }
};