两个字符串的删除操作
力扣: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
- word 和 word2只包含小写英文字母
本题和动态规划:115.不同的子序列相比,其实就是两个字符串都可以删除了,情况虽说复杂一些,但整体思路是不变的。这次是两个字符串可以相互删了,这种题目也知道用动态规划的思路来解。
动规五部曲:
1. 确定dp数组以及下标的含义
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] = Math.min({dp[i - 1][j - 1] + 2, dp[i - 1][j] + 1, dp[i][j - 1] + 1});
但是其实情况一和情况二已经包含情况三了即递推公式可以直接写为: dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + 1;
3. dp数组如何初始化
从递推公式中,可以看出来,dp[i][0] 和 dp[0][j]是一定要初始化的。
dp[i][0]:word2为空字符串,以i-1为结尾的字符串word1要删除多少个元素,才能和word2相同呢,很明显dp[i][0] = i。
dp[0][j]的话同理,所以代码如下:
int n = word1.length();
int m = word2.length();
int[][] dp = new int[n + 1][m + 1];
for(int i = 1; i <= n; i++)dp[i][0] = i;
for(int i = 1; i <= m; i++)dp[0][i] = i;
4. 确定遍历顺序
从递推公式 dp[i][j] = dp[i][j] = Math.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. 举例推导dp数组
以上分析完毕,代码如下:
class Solution {
public int minDistance(String word1, String word2) {
int n = word1.length();
int m = word2.length();
int[][] dp = new int[n + 1][m + 1];
for(int i = 1; i <= n; i++)dp[i][0] = i;
for(int i = 1; i <= m; i++)dp[0][i] = i;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; 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], dp[i][j - 1]) + 1;
}
}
return dp[n][m];
}
}