题目来源
- leetcode
题目描述
题目解析
递归
共有三种变换方式,插入一个字符,删除一个字符,和替换一个字符
分析:
- 不可以用map统计字符出现的频率,因为字符串的顺序很重要
- [删除] 和[添加]是等价的。比如"qwe"与"qwer",删除"qwer"的r与给"qwe"添加一个r是等价的。所以在比较的时候,需要尝试三种操作,因为谁也不知道当前的操作会对后面产生什么样的营销。
对于当前比较的两个字符word1[i]
和word2[j]
:
- 如果两者相同,直接跳到下一个为止
- 如果两者不相同,有三种处理方法:
- 第一种:(在
word1[i]
)插入一个word2[j]
,那么word2[j]
的字符就跳过了,接着比较word1[i]
和word2[j + 1]
即可 - 第二种:将
word1[i]
字符直接删掉,接着比较word1[i+1]
和word2[j]
即可 - 第三者:将
word[i]
修改为word2[j]
,接着比较word1[i+1]
和word[j+1]
- 第一种:(在
问题是,这种操作的时间是指数级别的,我们加个缓存优化一下,就可以把时间给降下来了。
代码如下:
class Solution {
public:
int minDistance(string word1, string word2){
int m = word1.size(), n = word2.size();
std::vector<std::vector<int>> cache(m, std::vector<int>(n));
return helper(word1, 0, word2, 0, cache);
}
private:
int helper(std::string &word1, int i, std::string &word2, int j, … }
};
动态规划
(1)定义状态:
f[i][j]
表示将word1
的前i
个字符变成word2
的前j
个字符所需要进行的最少操作次数- 需要考虑 word1 或 word2 一个字母都没有,即全增加/删除的情况,所以预留
dp[0][j]
和dp[i][0]
(2)状态转移方程:对于f[i][j]
,考虑word1的第i个字符与word2的第j个字符,分为两种情况:
word1[i] == word2[j]
,则f[i][j] == f[i - 1][j - 1]
word1[i] != word2[j]
,我们有三种选择,替换、删除、插入:- 替换: 替换
word1
的第i
个字符或者替换word2
的第j
个字符,则f[i][j] == f[i - 1][j - 1] + 1
; - 删除: 删除
word1
的第i
个字符或者删除word2
的第j
个字符,则f[i][j] = min(f[i - 1][j], f[i][j - 1]) + 1
; - 插入: 在
word2[j]
后面添加word1[i]
或者在word1[i]
后添加word2[j]
,则f[i][j] =min(f[i - 1][j], f[i][j - 1]) + 1
;
- 替换: 替换
(3)计算顺序:
- 按顺序计算,当计算
dp[i][j]
时,dp[i - 1][j]
,dp[i][j - 1]
,dp[i - 1][j - 1]
均已经确定了
(4)dp如何初始化:从一个字符串变成空字符串,非空字符串的长度就是编辑距离
for (int i = 0; i <= len1; i++) {
dp[i][0] = i;
}
for (int j = 0; j <= len2; j++) {
dp[0][j] = j;
}
(5)考虑输出
- 输出:dp[len1][len2] 符合语义,即 word1[0…len) 转换成 word2[0…len2) 的最小操作数
(6)空间优化:
- 根据状态转移方程,当前要填写的单元格的数值,完全取决于它的左边一格、上边一格,左上边主对角线上一个的数值。如下图:
- 因此,有两种经典的空间优化方案:① 滚动数组;② 把主对角线上要参考的数值使用一个新变量记录下来,然后在一维表格上循环赋值。由于空间问题不是这道题的瓶颈,可以不做这样的空间优化。
class Solution {
public:
int minDistance(string word1, string word2) {
int m = word1.size(), n = word2.size();
vector<vector<int>> dp(m + 1, vector<int>(n + 1));
for (int i = 0; i <= m; ++i) dp[i][0] = i;
for (int i = 0; i <= n; ++i) dp[0][i] = 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 - 1], min(dp[i - 1][j], dp[i][j - 1])) + 1;
}
}
}
return dp[m][n];
}
};
- 怎么填表
- 关于动画