【代码随想录训练营第42期 Day45打卡 - 编辑距离问题 - LeetCode 115.不同的子序列 583. 两个字符串的删除操作 72. 编辑距离

本文主要是介绍【代码随想录训练营第42期 Day45打卡 - 编辑距离问题 - LeetCode 115.不同的子序列 583. 两个字符串的删除操作 72. 编辑距离,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

一、编辑距离问题总结

二、题目与题解

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

题目链接

题解:动态规划

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

题目链接

题解1:最长公共子序列变形

题解2:编辑问题模板

题目三:72. 编辑距离

题目链接

题解:动态规划

 三、小结


一、编辑距离问题总结

编辑距离问题是动态规划算法的一个重要应用,这类问题以 72. 编辑距离 - 力扣(LeetCode)最为经典。

这里对这类问题的处理做一个步骤上的小结(以 72.编辑距离为基准):

1. 定义dp数组

        定义一个二维数组dp,其中dp[i][j]表示字符串s1的前i个字符转换成字符串s2的前j个字符所需的最小编辑距离

2. dp数组状态初始化

        dp[0][j]:表示空字符串转换为s2的前j个字符,需要j次插入操作。

        dp[i][0]:表示s1的前i个字符转换为空字符串,需要i次删除操作。

3. 状态转移方程

对于dp[i][j],我们考虑s1[i-1]和s2[j-1](即s1的第i个字符和s2的第j个字符)的匹配情况:

如果匹配:s1[i-1] == s2[j-1],则无需编辑,dp[i][j] = dp[i-1][j-1]。

如果不匹配:s1[i-1] != s2[j-1],则需要考虑以下三种操作,并取最小值:

        插入:在s1中插入一个字符以匹配s2[j-1],dp[i][j] = dp[i][j-1] + 1。

        删除:删除s1中的字符s1[i-1],dp[i][j] = dp[i-1][j] + 1。

        替换:将s1中的字符s1[i-1]替换为s2[j-1],dp[i][j] = dp[i-1][j-1] + 1。

4. 遍历顺序

动态规划的顺序是从左到右,从上到下计算dp数组。 -- 正序

5. 返回结果

最终,dp[s1.length()][s2.length()]将给出将整个s1转换为s2所需的最小编辑距离。

这便是对于这类问题的小结,其实也是对于今天打卡题目 72. 编辑距离 - 力扣(LeetCode)的小结。我们在处理这类问题的时候可以按照这样的思路进行。

二、题目与题解

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

题目链接

115. 不同的子序列 - 力扣(LeetCode)

给你两个字符串 s 和 t ,统计并返回在 s 的 子序列 中 t 出现的个数,结果需要对 109 + 7 取模。

示例 1:

输入:s = "rabbbit", t = "rabbit"
输出3
解释:
如下所示, 有 3 种可以从 s 中得到 "rabbit" 的方案
。
rabbbit
rabbbit
rabbbit

示例 2:

输入:s = "babgbag", t = "bag"
输出5
解释:
如下所示, 有 5 种可以从 s 中得到 "bag" 的方案。 
babgbag
babgbag
babgbag
babgbag
babgbag

提示:

  • 1 <= s.length, t.length <= 1000
  • s 和 t 由英文字母组成
题解:动态规划

这题和昨天打卡的 392. 判断子序列 - 力扣(LeetCode)相似。

昨天思路就按照上边总结的即可。

这里需要清楚状态方程的确立:

1.当前字符匹配(s[i - 1] 与 t[j - 1]),有两种情况 -- 这里求个数,即两种情况都得考虑:

                (1).不选择s[i-1],则子序列个数dp[i-1][j]

                (2).选择s[i-1],则子序列个数dp[i-1][j-1] -- 注意:这里是针对子序列的个数,而不是长度,在原有基础上多选择一个元素,个数不变,不用+1

2.当前字符不匹配,忽略(跳过)s当前字符 -- 昨天的打卡已经提到。

状态方程如下:

                if (s[i - 1] == t[j - 1]) {    dp[i][j] = dp[i - 1][j] + dp[i - 1][j - 1];}else {            //当前字符不匹配,忽略(跳过)s当前字符dp[i][j] = dp[i - 1][j];

这题还需要注意一个点:题目要求结果对 109 + 7 取模 ,但实际上最后是给数据开的足够大即可,我们对于 dp 数组开 unsigned long long 就行。

完整代码如下:

class Solution {
public:int numDistinct(string s, string t) {int n = s.size();int m = t.size();if (n < m) {          //子序列不可能长度更大return 0;}vector<vector<unsigned long long>> dp(n + 1, vector<unsigned long long>(m + 1, 0));   //dp[i][j]:以i-1为结尾的s子序列(s的前i个字符)中出现以j-1为结尾的t(t的前j个字符)的个数为dp[i][j]for(int i = 0; i <= n; i++) {       //初始化dp数组:当t为空字符串时,s中有1种方式匹配(即不选择任何字符)dp[i][0] = 1;} for(int i = 1; i <= n; i++) {for(int j = 1; j <= m; j++) {//当前字符匹配(s[i - 1] 与 t[j - 1]),有两种情况 -- 这里求个数,即两种情况都得考虑://1.不选择s[i-1],则子序列个数dp[i-1][j]//2.选择s[i-1],则子序列个数dp[i-1][j-1] -- 注意:这里是针对子序列的个数,而不是长度,在原有基础上多选择一个元素,个数不变,不用+1if (s[i - 1] == t[j - 1]) {    dp[i][j] = dp[i - 1][j] + dp[i - 1][j - 1];}else {            //当前字符不匹配,忽略(跳过)s当前字符dp[i][j] = dp[i - 1][j];}}}return dp[n][m];}
};

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

题目链接

583. 两个字符串的删除操作 - 力扣(LeetCode)

给定两个单词 word1 和 word2 ,返回使得 word1 和  word2 相同所需的最小步数

每步 可以删除任意一个字符串中的一个字符。

示例 1:

输入: word1 = "sea", word2 = "eat"
输出: 2
解释: 第一步将 "sea" 变为 "ea" ,第二步将 "eat "变为 "ea"

示例  2:

输入:word1 = "leetcode", word2 = "etco"
输出:4

提示:

  • 1 <= word1.length, word2.length <= 500
  • word1 和 word2 只包含小写英文字母
题解1:最长公共子序列变形

我们之前打卡过 1143. 最长公共子序列 - 力扣(LeetCode) 这一道题。

而现在这一道题其实就可以理解为之前那道的变形:题目通过删除元素使两者相同且使删除的步数最少,那么我们可以发现完成删除元素之后即是原来两个字符串的最长公共子序列 -- 这时,最小步数就为:两单词的长度和 - 2 * 最长公共子序列长度。

理清题意后,这题就简单了:

class Solution {
public:int minDistance(string word1, string word2) {int n = word1.size();int m = word2.size();//求最长公共子序列长度:dp[n][m]vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));       //dp[i][j]:word1 前 i 个字符(0, i-1)与 word2 前 j 个字符(0, j-1)的最长公共子序列的长度for (int i = 1; i <= n; i++) {for (int j = 1; j <= m; 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 n + m - 2*dp[n][m];  //最小步数:(n - dp[n][m]) + (m - dp[n][m])}
};
题解2:编辑问题模板

这里其实就跟上边总结的内容几乎一致。

需要理解的一个点就是:word1 插入一个元素等价于 word2 删除一个元素,这里只有删除操作,但完全可以按照删除和插入两个操作来看,这样基本就和总结的模板差不多。

对于字符不匹配时,有以下几种情况:

         1.删除word1[i - 1],最少操作次数为dp[i - 1][j] + 1

         2.删除word2[j - 1],最少操作次数为dp[i][j - 1] + 1

         3.同时删除word1[i - 1]和word2[j - 1],操作的最少次数为dp[i - 1][j - 1] + 2 -- 情况3实则被包含在前两种情况内,且步数更多,故取最小步数时,可以不考虑

代码如下:

class Solution {
public:int minDistance(string word1, string word2) {int n = word1.size();int m = word2.size();vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));   //dp[i][j]:以i-1为结尾(前 i 个元素)的字符串word1,和以j-1为结尾(前 j 个元素)的字符串word2,想要达到相等,所需要删除元素的最小步数//初始化dp数组for (int i = 1; i <= n; i++) {  //当word2为空字符串时,将word1转换为word2需要删除i个字符 -- 全部删掉dp[i][0] = i;}for (int j = 1; j <= m; j++) {      //当word1为空字符串时,将word2转换为word1需要删除j个字符 -- 全部删掉dp[0][j] = j;}for (int i = 1; i <= n; i++) {for (int j = 1; j <= m; j++) {if (word1[i - 1] == word2[j - 1]) {         //当前字符匹配(相同),则无需删除dp[i][j] = dp[i - 1][j - 1];} //当前字符不匹配(不同)时,删除元素共有3种情况://  1.删除word1[i - 1],最少操作次数为dp[i - 1][j] + 1//  2.删除word2[j - 1],最少操作次数为dp[i][j - 1] + 1//  3.同时删除word1[i - 1]和word2[j - 1],操作的最少次数为dp[i - 1][j - 1] + 2 -- 情况3实则被包含在前两种情况内,且步数更多,故取最小步数时,可以不考虑else {     dp[i][j] = min(dp[i - 1][j] + 1, dp[i][j - 1] + 1);}}}return dp[n][m];}
};

题目三:72. 编辑距离

题目链接

72. 编辑距离 - 力扣(LeetCode)

给你两个单词 word1 和 word2, 请返回将 word1 转换成 word2 所使用的最少操作数  。

你可以对一个单词进行如下三种操作:

  • 插入一个字符
  • 删除一个字符
  • 替换一个字符

示例 1:

输入:word1 = "horse", word2 = "ros"
输出:3
解释:
horse -> rorse (将 'h' 替换为 'r')
rorse -> rose (删除 'r')
rose -> ros (删除 'e')

示例 2:

输入:word1 = "intention", word2 = "execution"
输出:5
解释:
intention -> inention (删除 't')
inention -> enention (将 'i' 替换为 'e')
enention -> exention (将 'n' 替换为 'x')
exention -> exection (将 'n' 替换为 'c')
exection -> execution (插入 'u')

提示:

  • 0 <= word1.length, word2.length <= 500
  • word1 和 word2 由小写英文字母组成
题解:动态规划

这就是总结的经典模板题,重点就是删除,插入,替换三个操作的使用。

代码如下(含详细注释):

class Solution {
public:int minDistance(string word1, string word2) {int n = word1.size();int m = word2.size();vector<vector<int>> dp(n + 1,vector<int>(m + 1, 0));    //dp[i][j]:以i-1为结尾(前 i 个元素)的字符串word1,和以j-1为结尾(前 j 个元素)的字符串word2,想要达到相等,所需要操作的最小步数/*  初始化dp数组  */for (int i = 1; i <= n; i++) {              //word2为空时,word1删除当前所有元素(i)转换为word2dp[i][0] = i;}for (int j = 1; j <= m; j++) {      //word1为空时,word2删除当前所有元素(j)转换为word1dp[0][j] = j;}for (int i = 1; i <= n; i++) {for (int j = 1; j <= m; j++) {if (word1[i - 1] == word2[j - 1]) {         //当前字符匹配,则无需编辑dp[i][j] = dp[i - 1][j - 1];}/*当前字符不匹配,有三种情况:1.删除word1[i - 1],即dp[i - 1][j] + 1 -- 等价于在word2中插入元素word1[i - 1] 2.删除word2[j - 1],即dp[i][j - 1] + 1 -- 等价于在word1中插入元素word2[j - 1]3.替换word1的第i个字符 word1[i - 1] 与 word2的第j个字符 word2[j - 1],即dp[i - 1][j - 1] + 1取三者最小值,即是最小操作数*/else {dp[i][j] = min(dp[i - 1][j - 1] + 1, min(dp[i - 1][j] + 1, dp[i][j - 1] + 1));}}}return dp[n][m];}
};

 三、小结

昨天比较忙,今天是补的昨天的打卡,后边会继续坚持练习并打卡。

这篇关于【代码随想录训练营第42期 Day45打卡 - 编辑距离问题 - LeetCode 115.不同的子序列 583. 两个字符串的删除操作 72. 编辑距离的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1124990

相关文章

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.

mybatis和mybatis-plus设置值为null不起作用问题及解决

《mybatis和mybatis-plus设置值为null不起作用问题及解决》Mybatis-Plus的FieldStrategy主要用于控制新增、更新和查询时对空值的处理策略,通过配置不同的策略类型... 目录MyBATis-plusFieldStrategy作用FieldStrategy类型每种策略的作

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

linux下多个硬盘划分到同一挂载点问题

《linux下多个硬盘划分到同一挂载点问题》在Linux系统中,将多个硬盘划分到同一挂载点需要通过逻辑卷管理(LVM)来实现,首先,需要将物理存储设备(如硬盘分区)创建为物理卷,然后,将这些物理卷组成... 目录linux下多个硬盘划分到同一挂载点需要明确的几个概念硬盘插上默认的是非lvm总结Linux下多

使用 sql-research-assistant进行 SQL 数据库研究的实战指南(代码实现演示)

《使用sql-research-assistant进行SQL数据库研究的实战指南(代码实现演示)》本文介绍了sql-research-assistant工具,该工具基于LangChain框架,集... 目录技术背景介绍核心原理解析代码实现演示安装和配置项目集成LangSmith 配置(可选)启动服务应用场景

Python Jupyter Notebook导包报错问题及解决

《PythonJupyterNotebook导包报错问题及解决》在conda环境中安装包后,JupyterNotebook导入时出现ImportError,可能是由于包版本不对应或版本太高,解决方... 目录问题解决方法重新安装Jupyter NoteBook 更改Kernel总结问题在conda上安装了

pip install jupyterlab失败的原因问题及探索

《pipinstalljupyterlab失败的原因问题及探索》在学习Yolo模型时,尝试安装JupyterLab但遇到错误,错误提示缺少Rust和Cargo编译环境,因为pywinpty包需要它... 目录背景问题解决方案总结背景最近在学习Yolo模型,然后其中要下载jupyter(有点LSVmu像一个

Python如何计算两个不同类型列表的相似度

《Python如何计算两个不同类型列表的相似度》在编程中,经常需要比较两个列表的相似度,尤其是当这两个列表包含不同类型的元素时,下面小编就来讲讲如何使用Python计算两个不同类型列表的相似度吧... 目录摘要引言数字类型相似度欧几里得距离曼哈顿距离字符串类型相似度Levenshtein距离Jaccard相

0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型的操作流程

《0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeekR1模型的操作流程》DeepSeekR1模型凭借其强大的自然语言处理能力,在未来具有广阔的应用前景,有望在多个领域发... 目录0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型,3步搞定一个应

解决jupyterLab打开后出现Config option `template_path`not recognized by `ExporterCollapsibleHeadings`问题

《解决jupyterLab打开后出现Configoption`template_path`notrecognizedby`ExporterCollapsibleHeadings`问题》在Ju... 目录jupyterLab打开后出现“templandroidate_path”相关问题这是 tensorflo