• leetCode 1143.最长公共子序列 动态规划


    1143. 最长公共子序列 - 力扣(LeetCode)

    给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0 。

    一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。

    • 例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。

    两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。

    示例 1:

    输入:text1 = "abcde", text2 = "ace" 
    输出:3  
    解释:最长公共子序列是 "ace" ,它的长度为 3 。
    

    示例 2:

    输入:text1 = "abc", text2 = "abc"
    输出:3
    解释:最长公共子序列是 "abc" ,它的长度为 3 。
    

    示例 3:

    输入:text1 = "abc", text2 = "def"
    输出:0
    解释:两个字符串没有公共子序列,返回 0 。

     >>思路和分析

    本题和 leetCode 718.最长重复子数组 区别在于这里不要求是连续的了,但是要有相对顺序,即:"ace" 是 "abcde" 的子序列 ,但是 "aec" 不是 "abcde" 的子序列 

    >>动规五部曲

    1.确定dp数组(dp table)以及下标的含义

    dp[i][j] : 长度为 [0,i-1] 字符串 text1 与长度为 [0,j-1]字符串text2最长公共子序列为dp[i][j]

    2.确定递推公式

    思考:有哪些方向可以推出dp[i][j]

    • text1[i-1] == text[j-1]时,dp[i][j]=dp[i-1][j-1]+1
    • text1[i-1] != text[j-1]时,分两种情况讨论:
      • 情况①: 不看e了,考虑c,就是abc和ac。这两个原字符串的最长公共子序列也可能是abc和ac的最长公共子序列。因为c和e明显不相同,那么可不考虑e了
      • 情况②: 同理,也可以不看c了,考虑e,就是ab和ace。这两个字符串也可能是两个原字符串的最长公共子序列
      • 那么这两种情况应该怎么取呢?这两种情况都有可能是dp[i][j],那么
        • dp[i][j] = max(dp[i][j-1],dp[i-1][j]);
          • 情况①对应dp[i][j-1]
          • 情况②对应dp[i-1][j]
    1. 确定递推公式:
    2. if(text1[i-1] == text2[j-1]) dp[i][j] = dp[i-1][j-1] + 1;
    3. else dp[i][j] = max(dp[i-1][j],dp[i][j-1]);

    3.dp数组初始化

    • dp[i][0] 应该初始化为0,因为 test1[0,i-1] 空串最长公共子序列是0
    • dp[0][j] 同理也为0
    • 其他下标都是随着递推公式逐步覆盖,初始为多少都可以

    故统一初始为0,代码如下:

    vectorint>> dp(text1.size() + 1, vector<int>(text2.size() + 1, 0));
    

    4.确定遍历顺序

    那么为了在递推的过程中,这三个方向都是经过计算的数值,所以要从前向后从上到下来遍历这个矩阵

    5.举例推导dp数组

    由上图可看到dp[text1.size()][text2.size()]为最终结果

    1. class Solution {
    2. public:
    3. int longestCommonSubsequence(string text1, string text2) {
    4. vectorint>> dp(text1.size() + 1, vector<int>(text2.size() + 1, 0));
    5. for (int i = 1; i <= text1.size(); i++) {
    6. for (int j = 1; j <= text2.size(); j++) {
    7. if (text1[i - 1] == text2[j - 1]) {
    8. dp[i][j] = dp[i - 1][j - 1] + 1;
    9. } else {
    10. dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
    11. }
    12. }
    13. }
    14. return dp[text1.size()][text2.size()];
    15. }
    16. };
    • 时间复杂度: O(n * m),其中 n 和 m 分别为 text1 和 text2 的长度
    • 空间复杂度: O(n * m)

    >>优化空间复杂度

    1. class Solution {
    2. public:
    3. // 滚动数组
    4. int longestCommonSubsequence(string text1, string text2) {
    5. vector<int> dp(text2.size() + 1, 0);
    6. for(int i=1;i<=text1.size();i++) {
    7. int pre = dp[0];
    8. for(int j=1;j<=text2.size();j++) {
    9. int tmp = dp[j];
    10. if(text1[i-1] == text2[j-1]) dp[j] = pre + 1;
    11. else dp[j] = max(dp[j-1],dp[j]);
    12. pre = tmp;
    13. }
    14. }
    15. return dp[text2.size()];
    16. }
    17. };
    • 时间复杂度: O(n * m),其中 n 和 m 分别为 text1 和 text2 的长度
    • 空间复杂度: O(m)

    参考文章和视频:

    动态规划子序列问题经典题目 | LeetCode:1143.最长公共子序列_哔哩哔哩_bilibili 代码随想录 (programmercarl.com)

    来自代码随想录课堂截图:

  • 相关阅读:
    解决 /bin/bash^M: bad interpreter: No such file or directory
    MySQL第一弹
    HTML5七夕情人节表白网页制作【幻化3D相册】HTML+CSS+JavaScript
    电脑版微信图片保存在哪个文件夹,如何一次性全选保存
    计算机网络:路由协议
    【Boost C++ 库】托管共享内存详解
    Matlab Simulink支持system generator插件
    Self -Supervised Learning
    4.javase_流程控制语句
    MES管理系统在电子行业的作用和效益
  • 原文地址:https://blog.csdn.net/weixin_41987016/article/details/133689692