动态规划 43. 最长回文子序列

动态规划 43. 最长回文子序列

516. 最长回文子序列 - 力扣(LeetCode)

代码随想录

难度 5 - 中等

太难了,依然不会做。看完题解只觉得恍然大悟原来如此,但是不看直接做就感觉定义和递推就跟挤牙膏一样挤不出,挤出来了也不一定对。

与动态规划 42. 回文子串-CSDN博客要形成对比

注意本题中,子序列的要求是:不一定连续

  • 思路:(摘录、修改自代码随想录)

    1. dp定义:(重要,因为这道题求的是长度,而不是回文子序列个数,所以这么设置)

      dp[i][j]:字符串s[i, j]范围内最长的回文子序列的长度为dp[i][j] ​。

    2. 递推:(重要)

      在判断回文子串的题目中,关键逻辑就是看s[i]​与s[j]​是否相同。

      • 如果s[i]s[j]相同,

        那么dp[i][j] = dp[i + 1][j - 1] + 2(增加了头和尾,所以长度增加2)

      • 如果s[i]s[j]不相同,

        说明s[i]​和s[j]​的同时加入 并不能增加[i,j]​区间回文子序列的长度,

        那么分别加入s[i]s[j] ​**看看哪一个可以组成最长的回文子序列。(重点)**

        加入s[j]​的回文子序列长度为dp[i + 1][j]​。

        加入s[i]​的回文子序列长度为dp[i][j - 1]​。

        那么dp[i][j]一定是取最大的,即:dp[i][j] = max(dp[i + 1][j], dp[i][j - 1])

      代码:

      if s[i] == s[j]: # 如果首尾相等,最长的回文子序列的长度增加2
          dp[i][j] = dp[i+1][j-1] + 2
      else: # 如果首尾不相等,则取去头或者去尾后的[i,j]区间内最大的最长回文子序列的长度
          dp[i][j] = max(dp[i+1][j], dp[i][j-1])
      
    3. dp初始化:(重要)

      首先要考虑当ij相同的情况,

      从递推公式:dp[i][j] = dp[i + 1][j - 1] + 2​可以看出,递推公式无法计算ij​相同时候的情况。所以需要手动初始化一下ij相同,那么dp[i][j]一定是等于1的,即:一个字符的回文子序列长度就是1

      其他情况,

      dp[i][j]初始为0就行,这样递推公式:dp[i][j] = max(dp[i + 1][j], dp[i][j - 1])​ 中dp[i][j]​才不会被初始值覆盖。

      dp = [[0] * len(s) for _ in range(len(s))]
      for i in range(len(s)): dp[i][i] = 1
      
    4. 遍历顺序:

      从递归公式中,可以看出,dp[i][j]​ 依赖于 dp[i + 1][j - 1] ​,dp[i + 1][j]​ 和 dp[i][j - 1]​,

      **所以遍历i的时候一定要从下到上遍历,这样才能保证下一行的数据是经过计算的**​

      j​的话,可以正常从左向右遍历。

      代码如下:

      for i in range(len(s) - 1, -1, -1): # 行索引i,对应s索引i
          for j in range(i+1, len(s)): # 列索引j,对应s索引j
              if s[i] == s[j]: # 如果首尾相等,最长的回文子序列的长度增加2
                  dp[i][j] = dp[i+1][j-1] + 2
              else: # 如果首尾不相等,则取去头或者去尾后的[i,j]区间内最大的最长回文子序列的长度
                  dp[i][j] = max(dp[i+1][j], dp[i][j-1])
      
  • 代码:

    • 时间复杂度: O(n^2)
    • 空间复杂度: O(n^2)
    class Solution:
        def longestPalindromeSubseq(self, s: str) -> int:
            # dp[i][j]:表示索引区间范围[i,j] (注意是左闭右闭)的s子串的最长的回文子序列的长度
            dp = [[0] * len(s) for _ in range(len(s))]
            for i in range(len(s)): dp[i][i] = 1
    
            for i in range(len(s) - 1, -1, -1): # 行索引i,对应s索引i
                for j in range(i+1, len(s)): # 列索引j,对应s索引j
                    if s[i] == s[j]: # 如果首尾相等,最长的回文子序列的长度增加2
                        dp[i][j] = dp[i+1][j-1] + 2
                    else: # 如果首尾不相等,则取去头或者去尾后的[i,j]区间内最大的最长回文子序列的长度
                        dp[i][j] = max(dp[i+1][j], dp[i][j-1])
    
            return dp[0][-1] # 注意返回值是索引区间为[0, len(s) -1]时的dp
    

至此,代码随想录中动态规划这一大章终于了结!!!

这么多天的狂做题目和狂写博客,希望能帮助我自己真正吃透动态规划吧。

(虽然其实现在肯定没有吃透来着,但是毕竟只是刷第一遍,所谓动态规划百题才能入门,这也才四五十题,还有机会!)

你可能感兴趣的:(小白的代码随想录刷题笔记,Mophead的小白刷题笔记,leetcode,python,代码随想录,动态规划)