力扣第583题 两个字符串的删除操作 c++ 动态规划 附Java代码
题目
中等
相关标签
给定两个单词 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
只包含小写英文字母
思路和解题方法
minDistance
函数用于计算两个字符串word1
和word2
之间的最小编辑距离。- 创建一个二维向量
dp
作为状态转移表,它的大小为word1.size()+1
行和word2.size()+1
列。- 初始化状态转移表的第一行和第一列,表示将一个字符串转换为空串所需的操作次数。
- 使用嵌套的循环遍历状态转移表的剩余部分。
- 如果
word1
的第i-1
个字符和word2
的第j-1
个字符相等,则当前位置的编辑距离与前一个位置相同,即dp[i][j] = dp[i-1][j-1]
。- 如果
word1
的第i-1
个字符和word2
的第j-1
个字符不相等,则当前位置的编辑距离为左边和上方两个位置中的最小值加一,即dp[i][j] = min(dp[i-1][j] + 1, dp[i][j-1] + 1)
。- 最后返回状态转移表右下角的值,即最终的最小编辑距离。
复杂度
时间复杂度:
O(mn)
时间复杂度为O(mn),其中m和n分别为word1和word2的长度。这是因为该算法需要填充一个m行n列的状态转移表,每个格子需要进行常数级别的操作,因此总时间复杂度为O(mn)。
空间复杂度
O(mn)
空间复杂度也是O(mn),因为该算法需要创建一个m+1行n+1列的二维向量dp来保存状态转移表。每个格子都需要保存一个整数值,因此占用的空间为O(mn)。
c++ 代码
class Solution {
public:
int minDistance(string word1, string word2) {
// 创建一个二维向量dp,用于存储状态转移表
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()];
}
};
附另一种C++代码
- 代码中使用动态规划的思想,通过构建一个二维的状态转移表
dp
来保存子问题的最优解。其中,dp[i][j]
表示word1
的前i
个字符和word2
的前j
个字符的最长公共子序列长度。- 首先,初始化状态转移表的第一行和第一列,它们都是零,表示一个空串与任意字符串的LCS长度都为零。
- 然后,从左上角开始逐行逐列填充状态转移表。如果当前字符相等,则当前位置的LCS长度为左上角格子的值加一;如果当前字符不相等,则当前位置的LCS长度为左边和上方两个格子中的最大值。
- 最后,返回将两个字符串变成相同序列所需删除字符的最少次数,即两个字符串的长度之和减去LCS长度的两倍。
class Solution {
public:
int minDistance(string word1, string word2) {
// 创建一个二维向量dp,用于保存子问题的最优解
vector<vector<int>> dp(word1.size()+1, vector<int>(word2.size()+1, 0));
// 使用嵌套循环遍历状态转移表dp
for (int i=1; i<=word1.size(); i++){
for (int j=1; j<=word2.size(); j++){
if (word1[i-1] == word2[j-1]) {
// 如果当前字符相同,则当前位置的LCS长度为左上角格子的值加一
dp[i][j] = dp[i-1][j-1] + 1;
}
else {
// 如果当前字符不同,则当前位置的LCS长度为左边和上方两个格子中的最大值
dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
}
}
}
// 返回word1和word2的长度之和减去LCS长度的两倍
return word1.size() + word2.size() - dp[word1.size()][word2.size()] * 2;
}
};
Java代码
1
- 创建一个二维数组
dp
,大小为len1 + 1
行和len2 + 1
列,其中len1
和len2
分别是word1
和word2
的长度。- 初始化dp数组的第一行和第一列,表示将一个字符串转换为空串所需的操作次数。
- 使用嵌套的循环遍历dp数组的剩余部分。
- 如果
word1
的第i-1
个字符和word2
的第j-1
个字符相等,则当前位置的最长公共子序列长度为左上方对角线的值加一,即dp[i][j] = dp[i - 1][j - 1] + 1
。- 如果
word1
的第i-1
个字符和word2
的第j-1
个字符不相等,则当前位置的最长公共子序列长度为左边和上方两个位置中的较大值,即dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1])
。- 最后返回
len1 + len2 - dp[len1][len2] * 2
,即将word1
和word2
的总长度减去最长公共子序列的长度的两倍。
class Solution { public int minDistance(String word1, String word2) { int len1 = word1.length(); int len2 = word2.length(); int[][] dp = new int[len1 + 1][len2 + 1]; // 初始化状态转移表的第一行和第一列,它们都是零,表示一个空串与任意字符串的LCS长度都为零。 for (int i = 0; i <= len1; i++) { dp[i][0] = 0; } for (int j = 0; j <= len2; j++) { dp[0][j] = 0; } // 使用嵌套循环遍历状态转移表dp for (int i = 1; i <= len1; i++) { for (int j = 1; j <= len2; j++) { if (word1.charAt(i - 1) == word2.charAt(j - 1)) { // 如果当前字符相同,则当前位置的LCS长度为左上角格子的值加一 dp[i][j] = dp[i - 1][j - 1] + 1; } else { // 如果当前字符不同,则当前位置的LCS长度为左边和上方两个格子中的最大值 dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); } } } // 返回word1和word2的长度之和减去LCS长度的两倍 return len1 + len2 - dp[len1][len2] * 2; } }
2.
- 创建一个二维数组
dp
,大小为word1.length() + 1
行和word2.length() + 1
列。- 初始化dp数组的第一行和第一列,表示将一个字符串转换为空串所需的删除字符操作次数。
- 使用嵌套的循环遍历dp数组的剩余部分。
- 如果
word1
的第i-1
个字符和word2
的第j-1
个字符相等,则当前位置的删除字符个数与左上方对角线的值相同,即dp[i][j] = dp[i - 1][j - 1]
。- 如果
word1
的第i-1
个字符和word2
的第j-1
个字符不相等,则当前位置的删除字符个数为左上方对角线的值加2(替换操作),或者左边和上方两个位置中的较小值加1(删除操作),即dp[i][j] = Math.min(dp[i - 1][j - 1] + 2, Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1))
。- 最后返回
dp[word1.length()][word2.length()]
,即最终的最小删除字符个数。
class Solution { public int minDistance(String word1, String word2) { // 创建一个二维数组dp,用于保存子问题的最优解 int[][] dp = new int[word1.length() + 1][word2.length() + 1]; // 初始化状态转移表的第一行和第一列 for (int i = 0; i < word1.length() + 1; i++) { dp[i][0] = i; } for (int j = 0; j < word2.length() + 1; j++) { dp[0][j] = j; } // 使用嵌套循环遍历状态转移表dp for (int i = 1; i < word1.length() + 1; i++) { for (int j = 1; j < word2.length() + 1; j++) { if (word1.charAt(i - 1) == word2.charAt(j - 1)) { // 如果当前字符相同,则当前位置的编辑距离等于左上角格子的值 dp[i][j] = dp[i - 1][j - 1]; } else { // 如果当前字符不同,则当前位置的编辑距离等于左上角、左边和上方三个格子中的最小值加上对应的操作次数 dp[i][j] = Math.min(dp[i - 1][j - 1] + 2, // 替换操作,编辑距离+2 Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1)); // 删除或插入操作,编辑距离+1 } } } // 返回word1和word2的长度之和减去编辑距离 return dp[word1.length()][word2.length()]; } }
觉得有用的话可以点点赞,支持一下。
如果愿意的话关注一下。会对你有更多的帮助。
每天都会不定时更新哦 >人< 。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)