【动态规划】两个数组的 dp 问题一

在这里插入图片描述

点赞👍👍收藏🌟🌟关注💖💖
你的支持是对我最大的鼓励,我们一起努力吧!😃😃

1.最长公共子序列

题目链接:1143. 最长公共子序列

题目描述:

在这里插入图片描述

算法原理:

1.状态表示

这道题是遇到新的类型的题,两个字符串的dp问题。并且求的是最长公共子序列,我们很多题的经验就是从这道题来到。我们先总结一下这个经验。

经验 + 题目要求

  1. 选取第一个字符串 [0, i] 区间以及第二个字符串 [0, j] 区间作为研究对象
  2. 根据题目要求,确定状态表示

先以这两段区间来研究,这道题研究的是最长公共子序列。也就是说先看看上面s1这个区间所有子序列,以及下面s2这个区间所有子序列,其中的最长子序列。

dp[i][j] 表示:s1 的 [0, i] 区间以及 s2 的[0, j] 区间内所有子序列中,最长公共子序列的长度。

在这里插入图片描述
如果以某个位置为结尾所有子序列,还有在去找子序列,时间复杂O(N^4),而我们这里的子序列是所有的子序列,比如是说 [0,i] 区间所有的子序列。这个子序列既包含以i为结尾,也包含不以i为结尾的。

2.状态转移方程

根据最后一个位置的情况,分情况讨论

如果最后一个位置字符相等 s[i] == s[j]

是不是可以先在s1 [ 0 , i-1] 和 s2 [0 , j - 1]区间内所有子序列找最长公共子序列长度 然后 加上 1就行了

在这里插入图片描述

如果最后一个位置字符相等 s[i] != s[j]

那么最长公共子序列一定不是以 i j 位置为结尾,也就是说最长公共子序列一定不是同时选择 i 和 j 位置。那可以这样选

  1. s1[0, i - 1],s2[0, j] ,不选 i
  2. s1[0, i],s2[0, j -1] ,不选 j
  3. s1[0, i - 1],s2[0, j - 1], i j都不选

在这里插入图片描述
我们一般找状态转移方程要把所有情况不重不漏都找完,情况1 和 情况2 都包含情况3。但是注意我们的状态表示是最长公共子序列的长度,我们要找的是多种情况的max。所有这里的重复不会影响。但是如果要找的是公共子序列的个数,那就有问题了。遇到了再说。还有情况1和情况2包括情况3,所以填表的时候情况3就不用考虑了。

在这里插入图片描述

3.初始化

关于字符串的 dp 问题: 空串是有研究意义的

比如 s1 选一个空串,s2 选一个空串,其实也是一个公共子序列,不过公共子序列长度为0。

引入空串的概念之后,会方便我们的初始化

s1 [0,i] 区间,i最小0,最差就是 [0,0]是一定有一个字符的,如何表示空串呢?可以在原始 dp 表上多来一行一列,第一行表示第一个字符串为空,第二行表示第二个字符串为空。

并且填表也不会越界了。而且第一行表示第一个字符串为空,所以最长公共子序为0,第一列表示第二个字符串为空,所以最长公共子序也是0。

在这里插入图片描述

引入辅助节点要注意两个注意事项:

  1. 里面的值要保证我们后序填表是正确的
  2. 下标的映射关系

因为多开一行一列相当于dp表向右下移动一位,如果要回原表肯定是要 i-1,j-1。但是在字符串这里,我们可以在每个字符串前面加一个 ‘ ’,然后字符串有效字符就从1开始计数了。这个时候填表 1,1位置的时候就是对应字符串1,1位置。

4.填表顺序

填 i j 会遇到左上,上,左,因此从上往下填写每一行,每一行从左往右
在这里插入图片描述

5.返回值

dp[i][j] 表示:s1 的 [0, i] 区间以及 s2 的[0, j] 区间内所有子序列中,最长公共子序列的长度,而我们要的是s1,s2整个字符串的最长子序列的长度。因此 返回 dp[m][n]

class Solution {
   
public:
    int longestCommonSubsequence(string s1, string s2) {
   
        // 1.创建 dp 表
        // 2.初始化
        // 3.填表
        // 4.返回值

        int m = s1.size(), n = s2.size();
        s1 = ' ' + s1, s2 = ' ' + s2;// 处理下标映射关系
        vector<vector<int>> dp(m + 1, vector<int>(n + 1));// 建表 + 初始化
        for(int i = 1; i <= m; ++i) // 从上往下每⼀⾏
            for(int j = 1; j <= n; ++j)// 每⼀⾏从左往右
                //if(s1[i - 1] == s2[j - 1])
                if(s1[i] == s2[j])
                    dp[i][j] = dp
评论 93
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值