LeetCode72 编辑距离
LeetCode 字符串算法之 编辑距离
编辑距离
Difficulty: 困难
给定两个单词 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')
思路
动态规划
动态规划的状态定义 dp[i][j]
, 其中i
与j
都是各自字符串的终止字符的长度,不是索引
初始值
m\n | # | H | O | R | S | E |
---|---|---|---|---|---|---|
# | 0 | 1 | 2 | 3 | 4 | 5 |
R | 1 | |||||
O | 2 | |||||
S | 3 |
动态迁移方程
dp[i][j] = dp[i-1][j-1]; // word1[i-1] == word2[j-1]
dp[i][j] = min(min(dp[i][j-1],dp[i-1][j]),
dp[i-1][j-1]) + 1; // word1[i-1] != word2[j-1]
Solution
Language: C++
// 动态规划 , 划分子问题 -> 求解更大的问题
// 对于字符串的处理, 两个字符串: 有从尾部两两比较, 有从头两两比较 ;单字符串 从头,从尾,从中间处理
class Solution {
public:
int minDistance(string word1, string word2) {
int m = word1.size();
int n = word2.size();
// 小技巧判断
if (m * n == 0) return n + m;
// 1. 动态规划 的 状态定义 dp[i][j] , 其中i与j都是各自字符串的终止字符的长度,不是索引
// 两个字符串 word1[0-(i-1)] 与 word2[0-(j-1)] 的编辑距离 , 因为是长度,计算索引要用 长度-1
vector<vector<int>> dp(m+1,vector<int>(n+1,0));
// 从底向上推算
// 2. 初始化 状态的初始值
// 2.1 word1 为空时
for (int i =0; i <= m;i++) {
dp[i][0] = i;
}
// 2.2 word2 为空时
for (int i = 0; i <= n;i++) {
dp[0][i] = i;
}
// 3. 根据动态迁移方程更新状态
for (int i = 1; i <= m; i++) {
for (int j = 1; j<= n; j++) {
// 因为 i/j 代表的是长度, 所以 比较字符的时候要 -1
if (word1[i-1] == word2[j-1]) {
// 单词末尾相等, 则当前不需要更改
// 编辑距离等于两个word都缩减i,j字符后的距离
dp[i][j] = dp[i-1][j-1];
} else {
// 根据描述可以知道, i,j 字符不同时, 编辑的操作有下面几种, 这里是重点
// dp[i][j-1] word2 删除(j-1)索引下标的字符,重新比较
// dp[i-1][j] word2 插入一个[j]索引的字符, 重新比较
// dp[i-1][j-1] word2 替换一个[j-1]的字符, 重新比较
// 因为上面的步骤都是编辑了一次, 所有 dp[i][j] 的值是 小问题的编辑距离 +1
dp[i][j] = min(min(dp[i][j-1],dp[i-1][j]),
dp[i-1][j-1]) + 1;
}
}
}
// 4. 返回最后的值
return dp[m][n] ;
}
};