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

LeetCode-Problem516#较典型的二维动态规划问题

1)题目

在这里插入图片描述

2)分析(参考自@labuladong
  • 最长回文子串解法之一是考虑每个位置i对应字符char_i为回文子串的结尾,利用前面结果(主要是i-1位置的结果)递归求解;
  • 最长回文子序列想要按照同样思路去求解就比较麻烦,既然以字符结尾的方法求解遇阻,那么可以考虑区间求解,也是动态规划常见的另一种思路;区间求解即用 d p [ i ] [ j ] dp[i][j] dp[i][j]表示 s [ i : j ] s[i:j] s[i:j](包含 j j j,下同)的最长回文子序列 p a l pal pal,此时不要求 p a l pal pal s [ i ] s[i] s[i]开头或以 s [ j ] s[j] s[j]结尾;
  • 区间上的动态规划或者说递归,一般都是查看当前区间的前后区间,比如dp[i][j]和dp[i+1][j-1]、dp[i][j-1]、dp[i+1][j]等,看看是否形成递归关系,因此要往这个思路靠
  • 接下来是描述 d p [ i ] [ j ] dp[i][j] dp[i][j]的转移方程或者递归方程:
    d p [ i ] [ j ] = { d p [ i + 1 ] [ j − 1 ] + 2 if  s [ i ] = = s [ j ] m a x ( d p [ i ] [ j − 1 ] ,   d p [ i + 1 ] [ j ] ) if  s [ i ] ≠ s [ j ] dp[i][j]=\begin{cases} dp[i+1][j-1] + 2 &\text{if } s[i] == s[j]\\ max(dp[i][j-1],\ dp[i+1][j]) &\text{if } s[i] \ne s[j] \end{cases} dp[i][j]={dp[i+1][j1]+2max(dp[i][j1], dp[i+1][j])if s[i]==s[j]if s[i]=s[j]
  • 上述方程表示,子序列 s [ i , j ] s[i,j] s[i,j]的最长回文子序列存在下列两种情况:
    • 一是 s [ i ] = = s [ j ] s[i] == s[j] s[i]==s[j],那么 d p [ i ] [ j ] dp[i][j] dp[i][j] = d p [ i + 1 ] [ j − 1 ] dp[i+1][j-1] dp[i+1][j1] + 2,如下图(@labuladong)在这里插入图片描述
    • 二是 s [ i ] ≠ s [ j ] s[i] \ne s[j] s[i]=s[j],此时 s [ i ] s[i] s[i] s [ j ] s[j] s[j]最多只有一个对 d p [ i ] [ j ] dp[i][j] dp[i][j]有贡献,那么 d p [ i ] [ j ] = m a x ( d p [ i ] [ j − 1 ] , d p [ i + 1 ] [ j ] ) dp[i][j]=max(dp[i][j-1], dp[i+1][j]) dp[i][j]=max(dp[i][j1],dp[i+1][j]),如下图在这里插入图片描述
  • 需要处理初始值(base case);包括 i ≤ j i \le j ij(子序列存在条件)以及 d p [ i ] [ i ] = = 1 dp[i][i] == 1 dp[i][i]==1;此时整体思路可用下图表示:在这里插入图片描述
  • 图中左下角为0和对角线为1均是要满足base case;目标是求右上角值;为了利用动态规划,根据 d p [ i ] [ j ] dp[i][j] dp[i][j]的定义,有上图第二部分所示的两种回溯方法,这里选择第二种,横向+反向遍历,代码如下
class Solution:
    def longestPalindromeSubseq(self, s: str) -> int:
        # 参考自labuladong的解法,思路如下
        # 最长回文子串解法之一是以每个位置i对应字符c_i为回文子串的结尾,利用前面结果求解
        # 最长回文子序列想要按照同样思路去求解就比较麻烦,既然以字符结尾求解遇阻,那么可以考虑区间求解
        # 即用dp[i][j]表示s[i:j+1]的最长回文子序列pal,此时不要求pal以s[i]开头或以s[j]结尾
        # 接下来是描述dp[i][j]的转移方程或者递归方程
        # dp[i][j] = dp[i+1][j-1] + 2 if (s[i] == s[j]) else max(dp[i][j-1], dp[i+1][j])
        # base case: i <= j AND dp[i][i] == 1
        n = len(s)
        if n <= 1:
            return n
        dp = [[0 for _ in range(n)] for _ in range(n)]  # base case
        for i in range(n):  # base case
            dp[i][i] = 1
        for i in range(n-1, -1, -1):  # 反向遍历(回溯)
            for j in range(i+1, n):  # 横向遍历(回溯)
                dp[i][j] = dp[i+1][j-1] + 2 if (s[i] == s[j]) else max(dp[i][j-1], dp[i+1][j])
        return dp[0][n-1]
  • 总结:熟悉 2维区间动态规划的应用场景和应用方法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值