5. 最长回文子串 (longest palindromic substring)

3,904 阅读1分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第22天,点击查看活动详情

5. 最长回文子串 题目描述:给你一个字符串 s,找到 s 中最长的回文子串。

示例1示例2
输入s = "babad"
输出"bab"
解释"aba" 同样是符合题意的答案。
输入s = "cbbd"
输出"bb"

中规中矩的动态规划

达成共识:

1、单个字符一定是“回文串”;

2、如果字符串 ss[i+1,j1][i + 1,j - 1] 区间是回文串,且 s[i]==s[j]s[i] == s[j],那么 ss[i,j][i,j] 区间也必定是回文串,其中,0<=i<j<n0 <= i < j < nn=s.lengthn = s.length

1、确定 dp 状态数组

定义 dp[i][j]dp[i][j] 表示字符串 ss[i,j][i,j] 区间是否是回文串,每个元素值为 truetrue / falsefalse

🎡 NOTE: 这里是 左闭右闭 区间。

2、确定 dp 状态方程

这里要讨论一下 iijj 的相对大小:

  • [i+1,j1][i+1, j-1] 不能构成区间时,此时存在 ji<3j - i <3 (通过 j1(i+1)<1j - 1 - (i + 1) < 1 得到),此时 dp[i][j]dp[i][j] 是否能成为回文串仅仅取决于 s[i]==s[j]s[i] == s[j]

  • ji3j - i \ge 3 时,dp[i][j]dp[i][j] 不仅取决于 s[i]==s[j]s[i] == s[j],还要判断 dp[i+1][j1]dp[i + 1][j - 1] 是否是回文串,即 dp[i][j]dp[i][j] = (s[i]==s[j]) && dp[i+1][j1](s[i] == s[j]) \space \&\& \space dp[i + 1][j - 1]

3、确定 dp 初始状态

通过 单个字符一定是“回文串” 可知,dp[k][k]=truedp[k][k] = true,其中 k[0,n)k \in [0, n)

4、确定遍历顺序

  • 外层循环从 j=1j = 1 遍历 j=n1j = n- 1

  • 内层循环从 i=0i = 0 遍历到 i=j1i = j - 1

5、确定最终返回值

由于题目要求我们返回具体的回文串,所以我们需要额外记录一下最长回文串的 起始位置(begin)最大长度(maxLength); 每当 dp[i][j]==truedp[i][j] == true 而且 currLength>maxLengthcurrLength > maxLength 时,将 currLengthcurrLength 赋值给 maxLengthmaxLength,将 ii 赋值给 beginbegin,其中 currLength=ji+1currLength = j -i + 1

所以最终返回值为

s.slice(begin,begin+maxLength)s.slice(begin, begin+maxLength)

6、代码示例

/**
 * 空间复杂度 O(n^2)
 * 时间复杂度 O(n^2)
 */
function longestPalindrome(s: string): string {
    const n = s.length;

    if (n < 2) {
        return s;
    }

    const dp = Array.from({ length: n }, () => new Array(n).fill(false));
    let begin = 0;
    let maxLength = 1;

    for (let k = 0; k < n; k++) {
        dp[k][k] = true;
    }

    for (let j = 1; j < n; j++) {
        for (let i = 0; i < j; i++) {
            if (s[i] !== s[j]) {
                dp[i][j] = false;
            } else if (j - i < 3) {
                dp[i][j] = true;
            } else {
                dp[i][j] = dp[i + 1][j - 1];
            }

            const currLength = j - i + 1;
            if (dp[i][j] && currLength > maxLength) {
                begin = i;
                maxLength = currLength;
            }
        }
    }

    return s.slice(begin, begin + maxLength);
};

参考

# 重识动态规划