跳转至

5.最长回文子串 (Medium)*

题目描述*

标签*

字符串;动态规划;

思路 & 代码*

最简单的就是暴力枚举所有字符串,判断是否是回文,是则更新长度,时间复杂度 O(n^3),枚举平方加上线性判断回文。太慢了 8。

可以把字符串反转然后求公共子串,同时判断下标位置关系确定是不是回文串。最长公共子串长度,一瞅就是 dp。dp[i][j] 表示字符串 i 和 j 的最长公共子串长度。

还有一种 dp,dp[i][j] 表示 i 到 j 之间的子串是否是回文子串,则有 dp[i][j] = (s[i] == s[j] && dp[i + 1][j - 1]),需要注意的是要保证 i + 1 <= j - 1,枚举子串长度和起始位置即可。时间复杂度 O(n^2)。还可以倒着遍历 i。还可以优化空间,求第 i 行只需要 i + 1,j 只需要 j - 1,为了使用上一层的 j - 1,j 也要倒着遍历。

中心扩展法,回文串肯定是对称的,所以每次选一个中心往左右扩展,中心可以是一个或两个字符,因此一共有 n + n - 1 个中心。时间复杂度 O(n^2)

再就是 Manacher 算法,专门用来查找字符串最长回文子串的线性算法。首先通过在相邻字符间插入 #,然后在首尾添加 ^ 和 $,处理后的字符串长度恒为奇数 (n + n + 1)。用数组 p 保存从中心扩展的最大个数,也是源字符串的总长度。如对于处理过后的字符串 "^#c#b#c#b#c#$",p[6] = 5,表示从左边扩展 5 个字符,同时恢复到原字符串 "cbcbc" 的长度也为 5。由此对于中心 p[i] 处的字符串,原字符串起始位置为 (i - p[i]) / 2。

求 p[i] 的过程充分利用了回文串的对称性,以 c 为回文串中心,r 表示回文半径即 r = c + p[c],求 p[i],

class Solution {
public:
    string longestPalindrome(string s) {
        int len = s.length();
        if(len == 0) {
            return "";
        }
        vector<vector<int>> dp(len, vector<int>(len, 0));
        auto origin = s;
        reverse(s.begin(), s.end());
        int res = 0;
        int end = 0;
        for(int i = 0; i < len; i++) {
            for(int j = 0; j < len; j++) {
                if(origin[i] == s[j]) {
                    if(i == 0 || j == 0) {
                        dp[i][j] = 1;
                    }else {
                        dp[i][j] = dp[i - 1][j - 1] + 1;
                    }
                }
                // 满足条件还要判断下标是否符合条件,如 abc123cba。
                if(dp[i][j] > res && len - 1 - j + dp[i][j] - 1 == i) {
                    res = dp[i][j];
                    end = i;
                }
            }
        }
        return origin.substr(end - res + 1, res);
    }
};
class Solution {
public:
    string longestPalindrome(string s) {
        int len = s.length();
        vector<vector<bool>> dp(len, vector<bool>(len, false));
        string res = "";
        int maxLen = 0;
        for(int i = 1; i <= len; i++) {
            for(int start = 0; start < len; start++) {
                int end = start + i - 1;
                if(end >= len) {
                    break;
                }
                // 对于长度为 1 和 2 的直接初始化。
                dp[start][end] = ((i == 1 || i == 2 || dp[start + 1][end - 1]) && s[start] == s[end]);
                if(dp[start][end] && i > maxLen) {
                    maxLen = i;
                    res = s.substr(start, i);
                }
            }
        }
        return res;
    }
};
class Solution {
public:
    string longestPalindrome(string s) {
        int len = s.length();
        vector<vector<bool>> dp(len, vector<bool>(len, false));
        string res = "";
        int maxLen = 0;
        for(int i = len - 1; i >= 0; i--) {
            for(int j = i; j < len; j++) {
                dp[i][j] = (s[i] == s[j] && (j - i < 2 || dp[i + 1][j - 1]));
                if(dp[i][j] && j - i + 1 > maxLen) {
                    maxLen = j - i + 1;
                    res = s.substr(i, maxLen);
                }
            }
        }
        return res;
    }
};
class Solution {
public:
    string longestPalindrome(string s) {
        int len = s.length();
        vector<bool> dp(len, false);
        string res = "";
        int maxLen = 0;
        for(int i = len - 1; i >= 0; i--) {
            for(int j = len - 1; j >= i; j--) {
                dp[j] = (s[i] == s[j] && (j - i < 3 || dp[j - 1]));
                if(dp[j] && j - i + 1 > maxLen) {
                    maxLen = j - i + 1;
                    res = s.substr(i, maxLen);
                }
            }
        }
        return res;
    }
};
class Solution {
private:
    int expandAroundCenter(string &s, int l, int r) {
        int len = s.length();
        while(l >= 0 && r < len && s[l] == s[r]) {
            l--;
            r++;
        }
        return r - l - 1;
    }
public:
    string longestPalindrome(string s) {
        int len = s.length();
        if(len == 0) {
            return "";
        }
        int maxLen = 0;
        int res = 0;
        for(int i = 0; i < len; i++) {
            int len1 = expandAroundCenter(s, i, i);
            int len2 = expandAroundCenter(s, i, i + 1);
            int cur = max(len1, len2);
            if(cur > maxLen) {
                maxLen = cur;
                res = i - (cur - 1) / 2;
            }
        }
        return s.substr(res, maxLen);
    }
};

最后更新: July 23, 2022