【一看就会一写就废 指间算法】最长回文子串 —— 动态规划

74 阅读2分钟

指尖划过的轨迹,藏着最细腻的答案~

题目:

给你一个字符串 s,找到 s 中最长的回文子串。

示例 1:

输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。

示例 2:

输入:s = "cbbd"
输出:"bb"

提示:

1 <= s.length <= 1000
s 仅由数字和英文字母组成

分析:

动态规划一般分为3步走:

  • 确定dp数组含义: 我们定义dp[i][j]为从下标i到下标j的子串是否是回文串。

  • 状态转移方程: 寻找子问题,对于dp[i][j]来说,如果dp[i+1][j-1]为回文串,什么情况下dp[i][j]才会是回文串呢?例如:字符串"abcba","bcb"是回文串,两边相同时会是一个新的回文串,因此可得状态转移方程: dp[i][j]=dp[i+1][j1]&&(dp[i]==dp[j])dp[i][j] = dp[i+1][j-1] \&\& (dp[i] == dp[j])

  • 初始化: 我们思考一下,如果字符串长度为1,那直接就是回文串; 如果长度为2,是否可以使用上面状态转移方程呢,答案是不可以,我们没有办法分左右,所有当长度为2时,需要直接判断,两个字符相等即为回文串。

具体的实现时,需要两个循环:

  • 外层循环主要是遍历字符串的长度L,从3开始;
  • 内层循环遍历左边界i;右边界j = i + L - 1
  • 同时需要维护最长的子串长度maxLengeh=1;

AC代码:

class Solution {
public:
    string longestPalindrome(string s) {
        int n = s.size();
        if (n < 2) {
            return s;
        } else if (n == 2) {
            return s[0] == s[1] ? s : string(1, s[0]);
        }

        vector<vector<bool>> dp(n, vector<bool>(n, false));
        for (int i = 0; i < n; i++) {
            dp[i][i] = true;
        }

        int begin = 0, maxLength = 1;
        for (int L = 2; L <= n; L++) {
            for (int i = 0; i < n; i++) {
                int j = i + L - 1;
                if (j >= n) {
                    break;
                }


                if (s[i] == s[j]) {
                    if (j - i  < 3) {
                        dp[i][j] = true;
                    } else {
                        dp[i][j] = dp[i+1][j-1];
                    }
                }

                if (dp[i][j] && L > maxLength) {
                    maxLength = L;
                    begin = i;
                }
            }
        }

        return s.substr(begin, maxLength);
    }
};