给你两个单词 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')
编辑距离算法被数据科学家广泛应用,是用作机器翻译
和语音识别
评价标准的基本算法。
最直观的方法是暴力检查所有可能的编辑方法,取最短的一个。所有可能的编辑方法达到指数级,但我们不需要进行这么多计算,因为我们只需要找到距离最短的序列而不是所有可能的序列。
我们可以对任意一个单词进行三种操作:
插入一个字符;
删除一个字符;
替换一个字符。
题目给定了两个单词,设为 A 和 B,这样我们就能够六种操作方法。
但我们可以发现,如果我们有单词 A 和单词 B:
-
对单词 A 删除一个字符和对单词 B 插入一个字符是等价的。例如当单词 A 为 doge,单词 B 为 dog 时,我们既可以删除单词 A 的最后一个字符 e,得到相同的 dog,也可以在单词 B 末尾添加一个字符 e,得到相同的 doge;
-
同理,对单词 B 删除一个字符和对单词 A 插入一个字符也是等价的;
-
对单词 A 替换一个字符和对单词 B 替换一个字符是等价的。例如当单词 A 为 bat,单词 B 为 cat 时,我们修改单词 A 的第一个字母 b -> c,和修改单词 B 的第一个字母 c -> b 是等价的。
这样一来,我们可以保留一个字符串不变,只是操作另一个字符串。比如保留B,只操作A:
-
在单词 A 中
插入
一个字符 -
删除
A 中的一个字符 -
修改
A 中的一个字符
如下图,这里给出了最短编辑距离的其中一种编辑方式:
上图最短编辑距离为5。
可以发现操作不只有三个,其实还有第四个操作,就是什么都不要做(skip)。比如当 $s1[i] == s2[j] $ 时,执行 i − − i-- i−− 即 j − − j-- j−−
了解上述过程之后,下面就直接用动态规划的思路解题,定义动态规划数组的含义:
dp[i][j]
表示s1[0...i]
和 s2[0...j]
的最小编辑距离。
base case
就是 dp[..][0]
和 dp[0][..]
,表示当其中一个字符串0时,最小编辑距离肯定取决于不为0的那个字符串的长度,比如 A 为0,B 不为0,那么最短编辑距离有两种选择:
- 要么将 B 中的字符全部删除变为0
- 要么向 A 中逐个添加字符使其跟 B 相同
当 $s1[i] != s2[j] $,有三种选择:
-
在单词 A 中
插入
一个字符 -
删除
A 中的一个字符 -
修改
A 中的一个字符
只不过这三个选择哪一个,取决于选择哪一个使得dp[i][j]
,所以选择三个中的最小值
Java实现如下:
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];
// base case
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.charAt(i-1) == word2.charAt(j-1)){
dp[i][j] = dp[i-1][j-1];
}else{
dp[i][j] = min(
dp[i][j-1] + 1,
dp[i-1][j] + 1,
dp[i-1][j-1] + 1
);
}
}
}
return dp[m][n];
}
public static int min(int a, int b, int c){
return Math.min(a, Math.min(b, c));
}
}
时间复杂度:O(n^2)
空间复杂度:O(n^2)