动态规划 字符串最长回文子序列

90 阅读2分钟

题目

给定一个字符串,求字符串的最长回文子序列

  • 遍历模型为遍历每一个子串,dp[i][j],i、j表示从 i 到 j范围的子串,首先 i 不可能大于 j,当 i==j 时,区间长度为1,所以最长回文子序列全是1,当对角线的右边的斜线时即[i,i-1]范围时,区间长度为2,当 i 和 i-1位置相等时,最长回文子序列为2,否则为1

  • 对其余情况进行可能性分析,分为四种情况,在 [i,j]范围上,枚举出最长子序列可能出现的所有可能,虽然各个可能性区间会有重复,但保证了一定能统计出所有情况

    • 如果子串的最长回子序列和 i、j位置无关,则为 dp[i-1][j-1]
    • 如果子串的最长回文子序列和 i 有关,和 j 无关,则为 dp[i][j-1]
    • 如果子串的最长回文子序列和 i 无关,和 j 有关,则为 dp[i-1][j]
    • 如果子串的最长回文子序列以 i、j为首尾,则子序列为 dp[i-1][j-1]+2
  • 其余位置在填写的时候从左往右,从上往下一列一列填写

image.png

function process(str) {
  let row = str.length,
    col = str.length,
    max = 0;

  // 对角线长度为1
  for (let i = 0; i < row; i++) {
    dp[i][i] = 1;
  }
  // 对角线右边的斜线
  for (let i = 1; i < row; i++) {
    if (str[i] === str[i - 1]) {
      dp[i][i - 1] = 2;
    } else {
      dp[i][i - 1] = 1;
    }
  }

  // 其余情况进行可能性分析
  for (let i = 2; i < row; i++) {
    for (let j = 0; j <= i - 2; j++) {
      res = Math.max(res, dp[i - 1][j - 1]);
      res = Math.max(res, dp[i][j - 1]);
      res = Math.max(res, dp[i - 1][j]);
      if (str[i] === str[j]) {
        res = Math.max(res, dp[i - 1][j - 1] + 2);
      }
    }
  }

  return dp[0][row]; // 返回 [0,row] 范围的最长回文子序列就是答案
}