18. 最长回文子串【LC5】【一文搞定动态规划dp表】

347 阅读2分钟

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

示例 1:

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

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

核心思路:【动态规划】

动态规划:填dp表、当前ij状态、过去ij状态、如何联合得到输出、边界条件。

  • step1.定义状态:题目让我们求什么,就把什么设置为状态,题目求s中最长的回文子串,那就判断所有子串是否为回文子串,选出最长的。因此,dp[i][j]表示str[i:j+1]是否为回文子串(这里+1是为了构造闭区间);

  • step2.状态转移方程:对空间进行分类讨论(当前ij状态、过去ij状态 如何联合得到输出)

状态方程: dp[i][j] = str[i] === str[j] && dp[i+1][j-1]

 当前ij状态:头尾必须相等(s[i]==s[j])
 过去ij状态:去掉头尾之后还是一个回文(dp[i+1][j-1] is True)
 边界条件:只要是找过去ij状态的时候,就会涉及边界条件(即超出边界情况处理)

边界条件:

  1. 限定i <= j
  2. i==j时一定是回文
  3. j-1-(i+1)<=0,即j-i<=2时,只要当s[i]==s[j]时就是回文,不用判断dp[i+1][j-1]dp[i][j] 截取的子串。白话:长度为1,2,3的字符串,判定回文,只需要确认两边的字符是否相同即可;
  4. 避免j-1 > i+1 (需要由前序状态递推)
  • step3.初始状态:这里已经直接判断j-i<=2的情况了,因此用不到初始状态,可以不设

  • step4.输出内容:每次发现新回文都比较一下长度,记录i与长度

老样子,先整个样例,平铺一下所有解,再结合代码分析。

image.png

解:

var longestPalindrome = (str) => {
  if (!str) return '';
  let len = str.length;
  if (len === 1) {
    return str;
  }
  let start = 0;
  let max_len = 1;
  let cur_len = 0;
  let dpArr = Array.from(Array(len), () => new Array(len).fill('')) //创建dp表
  // 动态规划:填dp表、当前ij状态、过去ij状态、如何联合得到输出、边界条件
  // step1:定义状态:题目让我们求什么,就把什么设置为状态,题目求s中最长的回文子串,那就判断所有子串是否为回文子串,选出最长的
  // 因此:dp[i][j]表示str[i:j+1]是否为回文子串(这里+1是为了构造闭区间)
  // step2:状态转移方程:对空间进行分类讨论(当前ij状态、过去ij状态 如何联合得到输出)
  //     当前ij状态:头尾必须相等(s[i]==s[j])
  //     过去ij状态:去掉头尾之后还是一个回文(dp[i+1][j-1] is True)
  //     边界条件:只要是找过去ij状态的时候,就会涉及边界条件(即超出边界情况处理)
  for (let j = 0; j < len; j++) {
    for (let i = 0; i <= j; i++) { // 限定i <= j
      if (j - i <= 2) {
        // 边界条件,避免j-1 > i+1 (前面状态) 即i===j的情况,这种不适用递推式。
        // 长度为1,2,3的字符串,判定回文,只需要确认两边的字符是否相同即可。
        //     当i==j时一定是回文
        //     j-1-(i+1)<=0,即j-i<=2时,只要当s[i]==s[j]时就是回文,不用判断dp[i+1][j-1] 为dp[i][j] 截取的子串
        if (str[i] === str[j]) {
          dpArr[i][j] = true;
          cur_len = j - i + 1;
        }
      } else {
        if (str[i] === str[j] && dpArr[i + 1][j - 1]) { // j-i > 2就不会出现i+1 > j-1 的情况
          // 递推式
          dpArr[i][j] = true;
          cur_len = j - i + 1;
        }
      }
      // step3: 初始状态:这里已经直接判断j-i<=2的情况了,因此用不到初始状态,可以不设
      // step4: 输出内容:每次发现新回文都比较一下长度,记录i与长度
      if (dpArr[i][j]) {
        if (cur_len > max_len) {
          start = i;
          max_len = cur_len;
        }
      }
    }
  }
  return str.substr(start, max_len)
}

console.log(longestPalindrome("aacabdkacaa"))

———— 前端、Javascript实现、算法、刷题、leetcode