JAVA代码编写
583. 两个字符串的删除操作
给定两个单词 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.定义数组dp[i] [j]:以i-1为结尾的字符串word1,和以j-1位结尾的字符串word2,想要达到相等,所需要删除元素的最少次数。
2.确定递归公式
- 当word1[i - 1] 与 word2[j - 1]相同的时候
- 当word1[i - 1] 与 word2[j - 1]不相同的时候
当word1[i - 1] 与 word2[j - 1]相同的时候,dp[i] [j] = dp[i - 1] [j - 1];
当word1[i - 1] 与 word2[j - 1]不相同的时候,有三种情况:
情况一:删word1[i - 1],最少操作次数为dp[i - 1] [j] + 1
情况二:删word2[j - 1],最少操作次数为dp[i] [j - 1] + 1
情况三:同时删word1[i - 1]和word2[j - 1],操作的最少次数为dp[i - 1] [j - 1] + 2
那最后当然是取最小值,所以当word1[i - 1] 与 word2[j - 1]不相同的时候,递推公式:dp[i][j] = min({dp[i - 1] [j - 1] + 2, dp[i - 1] [j] + 1, dp[i] [j - 1] + 1});
因为 dp[i] [j - 1] + 1 = dp[i - 1] [j - 1] + 2,所以递推公式可简化为:dp[i] [j] = min(dp[i - 1] [j] + 1, dp[i] [j - 1] + 1);
3.数组初始化
dp[i] [0]:word2为空字符串,以i-1为结尾的字符串word1要删除多少个元素,才能和word2相同呢,很明显dp[i] [0] = i。
for (int i = 0; i <= len1; i++) {
for (int j = 0; j <= len2; j++) {
if (i == 0 || j == 0) {
dp[i][j] = i + j; // 如果一个字符串为空,那么需要的步数就是另一个字符串的长度
}
}
}
4.遍历顺序
从递推公式 dp[i] [j] = min(dp[i - 1] [j - 1] + 2, min(dp[i - 1] [j], dp[i] [j - 1]) + 1); 和dp[i] [j] = dp[i - 1] [j - 1]可以看出dp[i] [j]都是根据左上方、正上方、正左方推出来的。
所以遍历的时候一定是从上到下,从左到右,这样保证dp[i][j]可以根据之前计算出来的数值进行计算。
5.举个例子
以word1:“sea”,word2:"eat"为例,推导dp数组状态图如下:
![583.两个字符串的删除操作1](https://i-blog.csdnimg.cn/blog_migrate/9298598130251af03dad4f70acf7bba0.png)
复杂度分析:
- 时间复杂度:O(len1*len2)
- 空间复杂度:O(len1*len2)
public class MinStepsToEqualWords {
public int minDistance(String word1, String word2) {
int len1 = word1.length();
int len2 = word2.length();
// 创建一个二维数组dp,其中dp[i][j]表示将word1的前i个字符变为word2的前j个字符所需的最小步数
int[][] dp = new int[len1 + 1][len2 + 1];
// 初始化第一行和第一列
for (int i = 0; i <= len1; i++) {
for (int j = 0; j <= len2; j++) {
if (i == 0 || j == 0) {
dp[i][j] = i + j; // 如果一个字符串为空,那么需要的步数就是另一个字符串的长度
} else if (word1.charAt(i - 1) == word2.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1]; // 如果当前字符相同,则不需要额外步数
} else {
dp[i][j] = 1 + Math.min(dp[i - 1][j], dp[i][j - 1]); // 如果当前字符不同,取删除一个字符的最小步数
}
}
}
return dp[len1][len2];
}
public static void main(String[] args) {
MinStepsToEqualWords solution = new MinStepsToEqualWords();
System.out.println(solution.minDistance("sea", "eat")); // 输出: 2
System.out.println(solution.minDistance("leetcode", "etco")); // 输出: 4
}
}
72. 编辑距离
给你两个单词 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
由小写英文字母组成
教程:https://programmercarl.com/0072.%E7%BC%96%E8%BE%91%E8%B7%9D%E7%A6%BB.html
方法一:动态规划
思路:
五部曲
1.定义数组dp[i] [j]:以i-1为结尾的word1单词转换成以j-1为结尾的word2单词的操作数数为dp[i] [j]。
2.确定递归公式
- 当word1[i - 1] 与 word2[j - 1]相同的时候
- 当word1[i - 1] 与 word2[j - 1]不相同的时候
当word1[i - 1] 与 word2[j - 1]相同的时候,dp[i] [j] = dp[i - 1] [j - 1];
当word1[i - 1] 与 word2[j - 1]不相同的时候,有三种情况:
情况一:删word1[i - 1],最少操作次数为dp[i - 1] [j] + 1;
情况二:删word2[j - 1],最少操作次数为dp[i] [j - 1] + 1
情况三:同时删word1[i - 1]和word2[j - 1],操作的最少次数为dp[i - 1] [j - 1] + 2
情况四:那么只需要一次替换的操作,就可以让 word1[i - 1] 和 word2[j - 1] 相同。所以 dp[i][j] = dp[i - 1][j - 1] + 1;
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;
}
3.数组初始化
dp[0] [j] = j
dp[i] [0] = i
4.遍历顺序
可以看出dp[i][j]是依赖左方,上方和左上方元素的
5.举个例子
以示例1为例,输入:word1 = "horse", word2 = "ros"
为例,dp矩阵状态图如下:
![72.编辑距离1](https://i-blog.csdnimg.cn/blog_migrate/a28418226b4124d4bb9dff8c8adb5cd7.jpeg)
复杂度分析:
- 时间复杂度:O(mn)
- 空间复杂度:O(mn)
class Solution {
public int minDistance(String word1, String word2) {
int m = word1.length();
int n = word2.length();
int[][] dp = new int[m + 1][n + 1];
for (int i = 0; i <= m; i++) {
for (int j = 0; j <= n; j++) {
// 因为dp数组有效位从1开始
// 所以当前遍历到的字符串的位置为i-1 | j-1
if(i==0||j==0){
dp[i][j] = i+j;
}
else if (word1.charAt(i - 1) == word2.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1];
} else {
dp[i][j] = Math.min(Math.min(dp[i - 1][j - 1], dp[i][j - 1]), dp[i - 1][j]) + 1;
}
}
}
return dp[m][n];
}
}