帕先生之回文子序列问题 LeetCode516

87 阅读2分钟

1、 题目概述

给你一个字符串 s ,找出其中最长的回文子序列,并返回该序列的长度。

子序列: 字符串可以不连续

2、 动态规划解决思路

1、 用二维数组dp[i][j]来表示从i到j范围内最长回文子序列的长度

2、 初始化: 当ij相等的时候, 即指向同一个字符, 此时的dp值为1(因为单个字符本身就是回文的, 且长度为1), 其余均初始化为0

3、 推导公式:

  • 一头一尾相等, 即s[i] == s[j], 那么只需要求dp[i+1][j-1](去头去尾后的最长子序列长度)
dp[i][j]=dp[i+1][j1]+2dp[i][j] = dp[i+1][j-1] + 2
  • 一头一尾不相等, 即s[i] != s[j], 有两种情况, 两者取max
    • 去掉字符串头, 求剩下的最长子序列长度, 即[i+1, j]
    • 去掉字符串尾, 求剩下的最长子序列长度, 即[i, j-1]
dp[i][j]=max(dp[i+1][j],dp[i][j1])dp[i][j] = max(dp[i+1][j], dp[i][j-1])
  1. 遍历顺序 (二维数组横向为i,纵向为j

1.png

可以看到,如果要求出dp[i][j]的值, 需要由左下方, 左边和下面推导得到, 那么i的遍历顺序是从下往上(先要知道i+1的值才能推导出来i的值), j的遍历顺序是从左往右, 且 j的位置从i+1开始(j肯定大于i, 其次在初始化的时候已经判断过 ji相等的情况了)

5、 返回结果:因为dp表示的是从ij的闭区间范围, 所以直接返回dp[0][s.length -1]

3、 代码实现

class Solution:
    def longestPalindromeSubseq(self, s: str) -> int:
        length = len(s)
        dp = [[0] * length for _ in range(length)]
        # 单个字符的初始化为1, 即 i=j的情况
        for i in range(length):
            dp[i][i] = 1
        for i in range(length - 1, -1, -1): # 从下往上遍历i
            for j in range(i+1, length): # 从左往右遍历j, 同时j从i+1位置开始
                if s[i] == s[j]:
                    dp[i][j] = dp[i+1][j-1] + 2
                else:
                    dp[i][j] = max(dp[i][j-1], dp[i+1][j])
        return dp[0][length-1]