动态规划----最长回文子序列

1 题目

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

如果字符串的反序与原始字符串相同,则该字符串称为回文字符串。

示例 1:

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

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

提示:

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

2 分析

分析:
    最值问题---->dp
    判断是否回文串---->双指针两端判断是否相等
    回文子序列---->也是从两端开始判断---->二维dp[i][j]
    
step1 定义dp
    dp[i][j] 表示s[i,...,j] 字符串中的最长回文子序列的长度
step2 base case 
    dp[i][j] = 1  (i == j)
    dp[i][j] = 0  (i > j)
step3 递推关系
    s[i] == s[j]
        dp[i][j] = dp[i + 1][j - 1] + 2
    s[i] != s[j]
        dp[i][j] = max(dp[i + 1][j], dp[i][j - 1], dp(i + 1, j - 1)) 
step4 目标值 (dp函数中有s[i] == s[j], 所以为闭区间[])
    dp[0][len(s) - 1] 表示整个字符串的最长回文子序列的长度

暴力递归代码:

class Solution:
    def longestPalindromeSubseq(self, s: str) -> int:
      def dp(i, j):
          # base case
          if i == j:
              return 1
          if i > j:
              return 0

          # 递推关系
          if s[i] == s[j]:
              return dp(i + 1, j - 1) + 2
          else:
              return max(dp(i + 1, j), dp(i, j - 1), dp(i + 1, j - 1))

      return dp(0, len(s) - 1)
step5 重复子问题优化

    从图中可以看出递归过程中存在重复子问题。优化方法:备忘录 or dp table.
    备忘录添加:
      a 在递归函数的开头,查询备忘录中是否已经有值,如果有直接返回备忘录中的值。
      b 递归函数中所有更新递归值的地方,更新备忘录
      c 在递归函数的最后返回备忘录中的值
    dp table添加:
      a 将base case中的值更新到dp table中
      b 根据递推关系式,有方向的更新dp table
      c 返回dp table中的目标值

递归 + 备忘录 代码:

class Solution:
    def longestPalindromeSubseq(self, s: str) -> int:
      def dp(i, j):
          if i < j and memo[i][j] != 0:
              return memo[i][j]
          # base case
          if i == j:
              return 1
          if i > j:
              return 0

          # 递推关系
          if s[i] == s[j]:
              memo[i][j] = dp(i + 1, j - 1) + 2
          else:
              memo[i][j] = max(dp(i + 1, j), dp(i, j - 1), dp(i + 1, j - 1))
          
          return memo[i][j]

      memo = [[0 for j in range(len(s))]for i in range(len(s))]
      for i in range(len(s)):
          memo[i][i] = 1
      return dp(0, len(s) - 1)

dp table 代码:

class Solution:
    def longestPalindromeSubseq(self, s: str) -> int:
        dp = [[0 for j in range(len(s))] for i in range(len(s))]
        for i in range(len(s)):
            dp[i][i] = 1
        for i in range(len(s) - 2, -1, -1):
            for j in range(i + 1, len(s)):
                if s[i] == s[j]:
                    dp[i][j] = dp[i + 1][j - 1] + 2
                else:
                    dp[i][j] = max(dp[i + 1][j], dp[i][j - 1], dp[i + 1][j - 1])
        
        return dp[0][len(s) - 1]
posted @ 2023-06-02 00:40  柳叶昶  阅读(10)  评论(0编辑  收藏  举报