题目
给你两个单词 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')
思路
做题之前,先了解一下编辑距离的定义(一下来源于百度百科):
编辑距离(Minimum Edit Distance,MED),由俄罗斯科学家 Vladimir Levenshtein 在1965年提出,也因此而得名 Levenshtein Distance。
编辑距离 是针对二个字符串(例如英文字)的差异程度的量化量测,量测方式是看至少需要多少次的处理才能将一个字符串变成另一个字符串。编辑距离可以用在自然语言处理中,例如拼写检查可以根据一个拼错的字和其他正确的字的编辑距离,判断哪一个(或哪几个)是比较可能的字。DNA也可以视为用A、C、G和T组成的字符串,因此编辑距离也用在生物信息学中,判断二个DNA的类似程度。Unix下的diff及patch即是利用编辑距离来进行文本编辑对比的例子。
在整题中,始终要有这样一个理解,假设将 word1
转化为 word2
至少需要 x
步,那么同样的,将 word2
转化为 word1
也至少需要 x
步,并且编辑距离的描述也不是题目描述的那样,我认为正确的描述应该是:word1
和 word2
相互转化最少需要多少步,也即
w
o
r
d
1
→
w
o
r
d
2
word1 \rightarrow word2
word1→word2 或者
w
o
r
d
2
→
w
o
r
d
1
word2 \rightarrow word1
word2→word1 至少需要多少步,这样对于不了解编辑距离定义的人来讲,不会产生只考虑
w
o
r
d
1
→
w
o
r
d
2
word1 \rightarrow word2
word1→word2 的误解;
下面开始讲正解:
我们采用动态规划的思想,这个是编辑距离算法所采用的思想,定义一个 dp[i][j]
数组,表示 word1[0:i]
转化为 word2[0:j]
所需要的最少步骤,有了这样的定义,这道题其实就很好理解了,为了方便实现动态规划,我们令 dp[][] = int[len1+1][len2+1]
,这样就能方便处理 word1
为空或者 word2
为空的情况,我们将本体的动态规划状态转移描述如下:
- 初始状态,即
word1
或者word2
为空时,分别对应dp[0][j] = j
和dp[i][0] = i
; word1[i] == word2[j]
,这种情况下我们只需考虑word1[0:i-1]
和word[0:j-1]
之间的编辑距离,所以有dp[i][j] = dp[i-1][j-1]
;word[i] != word[j]
,这种情况稍微复杂一些,需要分三种情况讨论:- 先计算
word1[0:i]
与word1[0:j-1]
的编辑距离,再令word1[i+1] = word2[j]
,这样就实现了word1
到word2
的转换; - 与上述相反的过程,先计算
word1[0:i-1]
与word1[0:j]
的编辑距离,再令word1[i] = word2[j+1]
,这看似是将word2
转化为word1
的过程,但实际上如上面所说,word1
和word2
之间的转化是相互的; - 先计算
word[0:i-1]
与word[0:j-1]
之间的编辑距离,对于word[i]
与word[j]
我们只需要替换其中之一即可;
所以在这种情况下,dp[i][j] = 1 + min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]
;
- 先计算
具体代码如下:
代码
public class Solution {
public int minDistance(String word1, String word2) {
int len1 = word1.length(), len2 = word2.length();
int[][] dp = new int[len1+1][len2+1];
for (int i=0; i<=len1; i++){
for (int j=0; j<=len2; j++){
if (i == 0){
dp[i][j] = j; // word1为空,只需添加j+1个字符得到word2
} else if (j == 0){
dp[i][j] = i; // word2为空,word1只需删除i+1个字符得到word2
} else if (word1.charAt(i-1) == word2.charAt(j-1)){
dp[i][j] = dp[i-1][j-1]; //word1[i]==word2[j],则只需看word1[0:i-1]如何得到word2[0:j-1]
} else {
dp[i][j] = 1 + Math.min(Math.min(dp[i][j-1], dp[i-1][j]), dp[i-1][j-1]); //个字符不同,则分三种情况,取最小值
}
}
}
return dp[len1][len2];
}
public static void main(String[] args) {
Solution solution = new Solution();
String word1 = "intention", word2 = "execution";
System.out.println(solution.minDistance(word1, word2));
}
}