欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 教育 > 幼教 > 代码随想录Day 45|leetcode题目:115.不同的子序列、583. 两个字符串的删除操作、72. 编辑距离

代码随想录Day 45|leetcode题目:115.不同的子序列、583. 两个字符串的删除操作、72. 编辑距离

2025/11/14 1:17:19 来源:https://blog.csdn.net/LuckyYH__/article/details/142291575  浏览:    关键词:代码随想录Day 45|leetcode题目:115.不同的子序列、583. 两个字符串的删除操作、72. 编辑距离

提示:DDU,供自己复习使用。欢迎大家前来讨论~

文章目录

  • 题目
    • 题目一: 115.不同的子序列
      • 解题思路:
      • 1. 确定dp数组(dp table)以及下标的含义
      • 2. 确定递推公式
      • 3. dp数组如何初始化
      • 4. 确定遍历顺序
      • 5. 举例推导dp数组
      • 代码实现
    • 题目二:583. 两个字符串的删除操作
      • 解题思路:
      • 1. 确定dp数组(dp table)以及下标的含义
      • 2. 确定递推公式
      • 3. dp数组如何初始化
      • 4. 确定遍历顺序
      • 5. 举例推导dp数组
      • 代码实现
      • 动态规划二(求最长公共子序列)
      • 代码实现
    • 题目三:72. 编辑距离
      • 解题思路
      • 1. 确定dp数组(dp table)以及下标的含义
      • 2. 确定递推公式
      • 3. dp数组如何初始化
      • 4. 确定遍历顺序
      • 5. 举例推导dp数组
      • 代码实现
    • 编辑距离总结篇
      • 编辑距离问题

动态规划Part12

题目

题目一: 115.不同的子序列

115. 不同的子序列

解题思路:

这道题目要求判断字符串s是否包含字符串t作为子序列。这里的子序列指的是,t的字符可以不连续,但顺序必须保持一致。解题思路使用动态规划,具体步骤如下:

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

定义二维数组dp[i][j],其中dp[i][j]表示以s的前i-1个字符为结尾的子序列中,出现以t的前j-1个字符为结尾的子序列的个数。

2. 确定递推公式

根据s[i - 1]t[j - 1]是否相等,分两种情况:

  • 相等dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j]。因为s[i - 1]可以匹配t[j - 1],也可以不匹配。
  • 不相等dp[i][j] = dp[i - 1][j]。因为s[i - 1]不匹配t[j - 1],只能忽略s[i - 1]

3. dp数组如何初始化

  • dp[i][0] = 1:任何字符串的子序列都至少包含空字符串,所以初始化为1。
  • dp[0][j] = 0:空字符串s不能包含任何非空字符串t作为子序列,所以初始化为0。
  • dp[0][0] = 1:空字符串是自身的子序列。

4. 确定遍历顺序

遍历顺序为从左到右,从上到下,确保每个dp[i][j]的值都是基于已经计算过的dp[i-1][j]dp[i-1][j-1]来计算的。

5. 举例推导dp数组

以具体例子(如s = "baegg"t = "bag")来手动计算dp数组,验证递推公式的正确性。

代码实现

class Solution {
public:int numDistinct(string s, string t) {vector<vector<uint64_t>> dp(s.size() + 1, vector<uint64_t>(t.size() + 1));for (int i = 0; i <= s.size(); i++) dp[i][0] = 1; // 空字符串的子序列计数为1for (int j = 1; j <= t.size(); j++) dp[0][j] = 0; // 空s不能包含非空tfor (int i = 1; i <= s.size(); i++) {for (int j = 1; j <= t.size(); j++) {if (s[i - 1] == t[j - 1]) {dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j]; // s[i-1]匹配t[j-1]或不匹配} else {dp[i][j] = dp[i - 1][j]; // s[i-1]不匹配t[j-1],忽略s[i-1]}}}return dp[s.size()][t.size()]; // 返回s的所有子序列中包含t的计数}
};

时间复杂度为O(n*m),空间复杂度也为O(n*m),其中n和m分别是字符串s和t的长度。

题目二:583. 两个字符串的删除操作

583. 两个字符串的删除操作

解题思路:

这道题目要求找出将两个字符串转换为彼此所需的最少删除次数,可以通过动态规划来解决。以下是详细的解题思路:

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

定义二维数组dp[i][j],其中dp[i][j]表示将字符串word1的前i-1个字符和字符串word2的前j-1个字符转换为彼此所需的最少删除次数。

2. 确定递推公式

根据word1[i - 1]word2[j - 1]是否相等,分两种情况:

  • 相等dp[i][j] = dp[i - 1][j - 1]。因为当两个字符相等时,不需要删除这两个字符。
  • 不相等dp[i][j] = min(dp[i - 1][j] + 1, dp[i][j - 1] + 1)。因为当两个字符不相等时,可以选择删除word1的一个字符或者删除word2的一个字符,取两者的最小值。

3. dp数组如何初始化

  • dp[i][0] = i:表示word2为空字符串时,需要删除word1的前i-1个字符。
  • dp[0][j] = j:表示word1为空字符串时,需要删除word2的前j-1个字符。

4. 确定遍历顺序

遍历顺序为从上到下,从左到右,确保每个dp[i][j]的值都是基于已经计算过的dp[i-1][j]dp[i][j-1]来计算的。

5. 举例推导dp数组

以具体例子(如word1 = "sea"word2 = "eat")来手动计算dp数组,验证递推公式的正确性。

代码实现

class Solution {
public:int minDistance(string word1, string word2) {vector<vector<int>> dp(word1.size() + 1, vector<int>(word2.size() + 1));for (int i = 0; i <= word1.size(); i++) dp[i][0] = i;for (int j = 0; j <= word2.size(); j++) dp[0][j] = j;for (int i = 1; i <= word1.size(); i++) {for (int j = 1; j <= word2.size(); j++) {if (word1[i - 1] == word2[j - 1]) {dp[i][j] = dp[i - 1][j - 1];} else {dp[i][j] = min(dp[i - 1][j] + 1, dp[i][j - 1] + 1);}}}return dp[word1.size()][word2.size()];}
};

时间复杂度为O(n*m),空间复杂度也为O(n*m),其中n和m分别是字符串word1和word2的长度。

动态规划二(求最长公共子序列)

另一种解法是求两个字符串的最长公共子序列(LCS),然后用两个字符串的总长度减去最长公共子序列的长度的两倍,即为最少删除次数。

代码实现

class Solution {
public:int minDistance(string word1, string word2) {vector<vector<int>> dp(word1.size()+1, vector<int>(word2.size()+1, 0));for (int i=1; i<=word1.size(); i++){for (int j=1; j<=word2.size(); j++){if (word1[i-1] == word2[j-1]) dp[i][j] = dp[i-1][j-1] + 1;else dp[i][j] = max(dp[i-1][j], dp[i][j-1]);}}return word1.size()+word2.size()-dp[word1.size()][word2.size()]*2;}
};

时间复杂度为O(n*m),空间复杂度为O(n*m)。


题目三:72. 编辑距离

72. 编辑距离

解题思路

编辑距离问题是一个经典的动态规划问题,用于计算两个字符串之间的最小编辑操作次数,使得一个字符串可以转换成另一个字符串。以下是详细的解题思路:

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

定义二维数组dp[i][j],其中dp[i][j]表示以下标i-1为结尾的字符串word1和以下标j-1为结尾的字符串word2之间的最小编辑距离。

2. 确定递推公式

根据word1[i - 1]word2[j - 1]是否相等,分两种情况:

  • 相等:不需要任何编辑操作,即dp[i][j] = dp[i - 1][j - 1]
  • 不相等:需要进行编辑操作,可能的操作包括:
    • 删除word1的一个字符:dp[i - 1][j] + 1
    • 删除word2的一个字符:dp[i][j - 1] + 1
    • 替换word1的一个字符:dp[i - 1][j - 1] + 1
    • 选择最小操作次数:dp[i][j] = min({dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1]}) + 1

3. dp数组如何初始化

  • dp[i][0] = i:表示将word1的前i-1个字符转换为一个空字符串需要删除i次。
  • dp[0][j] = j:表示将一个空字符串转换为word2的前j-1个字符需要添加j次。

4. 确定遍历顺序

遍历顺序为从上到下,从左到右,确保每个dp[i][j]的值都是基于已经计算过的dp[i-1][j]dp[i][j-1]dp[i-1][j-1]来计算的。

5. 举例推导dp数组

以具体例子(如word1 = "horse"word2 = "ros")来手动计算dp数组,验证递推公式的正确性。

代码实现

class Solution {
public:int minDistance(string word1, string word2) {vector<vector<int>> dp(word1.size() + 1, vector<int>(word2.size() + 1, 0));for (int i = 0; i <= word1.size(); i++) dp[i][0] = i;for (int j = 0; j <= word2.size(); j++) dp[0][j] = j;for (int i = 1; i <= word1.size(); i++) {for (int j = 1; j <= word2.size(); j++) {if (word1[i - 1] == word2[j - 1]) {dp[i][j] = dp[i - 1][j - 1];} else {dp[i][j] = min({dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1]}) + 1;}}}return dp[word1.size()][word2.size()];}
};

时间复杂度为O(n*m),空间复杂度也为O(n*m),其中n和m分别是字符串word1和word2的长度。

编辑距离总结篇

  1. 判断子序列:判断一个字符串是否为另一个字符串的子序列,只涉及删除操作。
  2. 不同的子序列:计算一个字符串在另一个字符串子序列中出现的次数,涉及删除操作。
  3. 两个字符串的删除操作:计算使两个字符串相同的最少删除次数,涉及两个字符串的删除操作。

编辑距离问题

问题定义:给定两个字符串word1word2,计算将word1转换成word2的最少操作数,操作包括插入、删除和替换。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词