算法日记36:leetcode095最长公共子序列(线性DP)

一、题目:

二、题解

1、解题思路:

1)通过观察我们发现,题目是典型的满足线性DP性质的题目,即满足

  • 最优子结构(子问题取最优,最终的大问题必然最优)
  • 无后效性(即当前下标i的取值与其后续下标(i~n)无关)
  • 子问题重叠性(解决了子问题的重叠产生的时间浪费,即空间换时间)

2)动态规划解题思路

  • dp[i][j] :来表示第一个串的前i位,第二个串的前j位的 L C S LCS LCS的长度
1#重述问题:
  • text1(0->len_1),text2(0->len_2)中选择最长公共子序列
2#找到最后一步:
  • text1中选择了text1[i]作为最后一位,从text2中选了text2[j]作为最后一位,使得公共子序列最长
3#去掉最后一步,问题变成了什么?
  • text1(0~i-1)text2(0~j-1)中选择最长公共子序列。
  • 原问题答案=dp[i-1][j-1]+(考虑i,j的情况)
4#考虑边界:
  • 数组结束

2、关键代码解析:

原问题答案=dp[i-1][j-1]+(考虑i,j的情况)

cpp 复制代码
      if(text1[1]==text2[1]) dp[1][1]=1;    //对第一位数初始化
      for(int i=1;i<=len1;i++)//遍历text1
      {
        for(int j=1;j<=len2;j++) //遍历text2
        {
          if(text1[i]==text2[j]) dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1);
          else dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
        }
      }
       return dp[len1][len2]; //到[len1][len2]的最长公共子序列

1#. 代码的作用

这段代码的作用是:

计算两个字符串 text1text2 之间的最长公共子序列的长度

什么是最长公共子序列?

最长公共子序列指的是,在不改变字符顺序的前提下,两个字符串中都出现的最长子序列

注意,子序列不同于子串,子序列不要求连续。


2.# 代码解析

(1)初始化 dp 数组
cpp 复制代码
memset(dp,0,sizeof(dp));
  • memset 函数用于将 dp 数组初始化为 0,确保 dp 里面的值不会影响后续计算。

(2)在 text1text2 前面添加一个空格
cpp 复制代码
text1=' '+text1;
text2=' '+text2;
  • 这里在 text1text2 前面添加了一个空格 ,这样 text1text2 的索引可以从 1 开始,避免处理 0 索引的特殊情况,提高代码可读性。

(3)获取字符串长度
cpp 复制代码
int len1=text1.size()-1,len2=text2.size()-1;
  • 这里计算 text1text2 的实际长度(去掉前面额外添加的空格)。

(4)初始化 dp[1][1]
cpp 复制代码
if(text1[1]==text2[1]) dp[1][1]=1;
  • 这个条件判断 text1text2 的第一个字符是否相同 ,如果相同,则 dp[1][1] = 1,表示最长公共子序列长度为 1

(5)动态规划填表
cpp 复制代码
for(int i=1;i<=len1;i++)// 遍历 text1
{
  for(int j=1;j<=len2;j++) // 遍历 text2
  {
    if(text1[i]==text2[j]) dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1);
    else dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
  }
}
  • 外层循环 遍历 text1 的每个字符。
  • 内层循环 遍历 text2 的每个字符。
  • 状态转移方程 :
    • 如果 text1[i] == text2[j] :

      cpp 复制代码
      dp[i][j] = max(dp[i][j], dp[i-1][j-1] + 1);
      • 说明当前字符匹配,最长公共子序列长度 +1
    • 如果 text1[i] != text2[j] :

      cpp 复制代码
      dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
      • 说明当前字符不匹配,我们可以选择:
        1. 不考虑 text1[i],用 dp[i-1][j] 的结果
        2. 不考虑 text2[j],用 dp[i][j-1] 的结果
        3. 取两者的 max,得到最长公共子序列的长度。

(6)返回最终答案
cpp 复制代码
return dp[len1][len2]; // 返回最长公共子序列的长度
  • dp[len1][len2] 存储了 text1 的前 len1 个字符和 text2 的前 len2 个字符的 最长公共子序列长度

3.# 复杂度分析

  • 时间复杂度 : O(n*m)nm 分别是 text1text2 的长度。
  • 空间复杂度 : O(n*m),因为 dp 数组占用了 O(n*m) 的空间。

如果使用滚动数组优化,可以降到 O(min(n, m)) 的空间复杂度。


3、完整代码解析:

cpp 复制代码
const int N=1007;
int dp[N][N];

class Solution {
public:
    int longestCommonSubsequence(string text1, string text2) 
    {
      memset(dp,0,sizeof(dp));
      text1=' '+text1;
      text2=' '+text2;
      int len1=text1.size()-1,len2=text2.size()-1;

      if(text1[1]==text2[1]) dp[1][1]=1;    //对第一位数初始化
      for(int i=1;i<=len1;i++)//遍历text1
      {
        for(int j=1;j<=len2;j++) //遍历text2
        {
          if(text1[i]==text2[j]) dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1);
          else dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
        }
      }
       return dp[len1][len2]; //到[len1][len2]的最长公共子序列
    }
};
相关推荐
吃好睡好便好13 小时前
查找函数的使用
学习·算法·matlab·生活·查找函数
学习星球13 小时前
单调栈——从“找下一个更大的“到柱状图中的最大矩形
数据库·c++·算法·leetcode·xcode
靠沿14 小时前
贪心算法专题(二)
算法·贪心算法
猎头南楼15 小时前
具身智能模型部署方向的能力要求与技术栈梳理
人工智能·深度学习·算法
啥都想学点的研究生16 小时前
一篇文章讲清楚:线性回归问题的求解——正规方程法
算法·机器学习·线性回归
青少儿编程课堂16 小时前
树状数组精讲:逆序对计数与离散化(附双语言解法)
算法·树状数组·离散化·信息学奥赛·逆序对
血小板要健康16 小时前
网格 dfs 与 FloodFill:从岛屿、区域到搜索路径
笔记·算法·leetcode·深度优先
VALENIAN瓦伦尼安教学设备17 小时前
设备状态检测振动分析实训台案例分析
大数据·数据库·人工智能·嵌入式硬件·算法
小小晓.17 小时前
C++ 值类别与完美转发深度剖析:从左值右值到 std::forward 源码解密
开发语言·c++·算法
怕浪猫17 小时前
用了两年 AI 编程工具后,我重新理解了什么是「资深工程师」
算法·面试·架构