母题
给你两个单词 word1 和 word2, 请返回将 word1 转换成 word2 所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
- 插入一个字符
- 删除一个字符
- 替换一个字符
示例 1:
输入:word1 = “horse”, word2 = “ros”
输出:3
解释:
horse -> rorse (将 ‘h’ 替换为 ‘r’)
rorse -> rose (删除 ‘r’)
rose -> ros (删除 ‘e’)
思考
这种问题好复杂啊,一拿上来,不会做啊,很尴尬。
假设s=“horse”, t=“rose”
我们从尾巴看起。
假设我们知道“hors”和“ros”的最小编辑距离为d,那么“horse”和“rose”的编辑距离就是d了,因为最后一个字符都相同了,还编辑个屁啊。
好了,现在转为求“hors”和“ros”的最小编辑距离了,我们又注意到最后字母都一样,所以d=distance(“hor”,“ro”)了。
现在“hor”和“ro”的编辑距离咋算呢?这个时候,他们的末尾字母不再相同了。
既然不同,那我们就通过编辑操作让他们的末尾相同呗。题目是支持删除、插入和替换的。
要通过“删除、插入、替换”操作让“hor”和“ro”的末尾相同,可以咋做呢?
- 删除“hor”的尾巴,然后计算“ho”和“ro”的最小编辑距离 加上这一步的代价1,此时两个串变成了“ho”和“ro”了。这个操作也等价于在“ro”的末尾插入r。
- 删除“ro”的尾巴,认为“ro”的“o”是多余的,此时计算“hor”和“r”的编辑距离,加上这一步的代价1。
- 同时删除两个串的尾巴,其实就是等价于替换两个串的尾巴为相同的字符,加上这一步的代价1。
然后我们再取这三个操作的最小就行了。
class Solution {
public:
int minDistance(string word1, string word2) {
vector< vector<short> > dp;
int m = word1.size();
int n = word2.size();
// 1. 初始化dp,dp应该包含[m+1,n+1]个元素
for(int i =0;i<=m;i++){
dp.push_back(vector<short> (n+1, 0));
for(int j =0;j<=n;j++){
// 2. dp[0,0..i]都应该初始化为i,
dp[0][j]=j;
}
// 3. dp[0..i, 0]都初始化为i。表示空字符串与长度为i的字符串比较。
dp[i][0] = i;
}
for(int i =1;i<=m;i++){
for(int j =1;j<=n;j++){
if(word1[i-1] == word2[j-1]){
dp[i][j] = dp[i-1][j-1];
}
else{
dp[i][j] = min(min(dp[i-1][j], dp[i][j-1]), dp[i-1][j-1]) + 1;
}
}
}
return dp[m][n];
}
};
变形1: 583 两个字符串的删除操作
https://leetcode-cn.com/problems/delete-operation-for-two-strings/
此时就只能删除,于是尾巴不同的时候只有两种操作:
- 删除“hor”的尾巴,然后计算“ho”和“ro”的最小编辑距离 加上这一步的代价1,此时两个串变成了“ho”和“ro”了。这个操作也等价于在“ro”的末尾插入r。
- 删除“ro”的尾巴,认为“ro”的“o”是多余的,此时计算“hor”和“r”的编辑距离,加上这一步的代价1。
class Solution {
public:
int minDistance(string word1, string word2) {
vector< vector<short> > dp;
int m = word1.size();
int n = word2.size();
// 1. 初始化dp,dp应该包含[m+1,n+1]个元素
for(int i =0;i<=m;i++){
dp.push_back(vector<short> (n+1, 0));
for(int j =0;j<=n;j++){
// 2. dp[0,0..i]都应该初始化为i,
dp[0][j]=j;
}
// 3. dp[0..i, 0]都初始化为i。表示空字符串与长度为i的字符串比较。
dp[i][0] = i;
}
for(int i =1;i<=m;i++){
for(int j =1;j<=n;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], dp[i][j-1]) + 1;
}
}
}
return dp[m][n];
}
};
变形2: 712. 两个字符串的最小ASCII删除和
https://leetcode-cn.com/problems/minimum-ascii-delete-sum-for-two-strings/
一样的分析,只是每一步删除、插入的代价不同了。
class Solution {
public:
int minDistance(string word1, string word2) {
vector< vector<int> > dp;
int m = word1.size();
int n = word2.size();
// 1. 初始化dp,dp应该包含[m+1,n+1]个元素
for(int i =0;i<=m;i++){
dp.push_back(vector<int> (n+1, 0));
for(int j =0;j<=n;j++){
// 2. dp[0,0..i]都应该初始化为i,
if(j>0)
dp[0][j]= dp[0][j-1] + word2[j-1];
else
dp[0][j]=0;
}
// 3. dp[0..i, 0]都初始化为i。表示空字符串与长度为i的字符串比较。
if(i>0)
dp[i][0] = dp[i-1][0] + word1[i-1];
else{
dp[i][0] = 0;
}
}
for(int i =1;i<=m;i++){
for(int j =1;j<=n;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] + word1[i-1], dp[i][j-1]+ word2[j-1]);
}
}
}
return dp[m][n];
}
int minimumDeleteSum(string s1, string s2) {
return minDistance(s1, s2);
}
};