题目:
给定两个单词 word1 和 word2,计算出将 word1 转换成 word2 所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
输入: word1 = "horse", word2 = "ros" 输出: 3 解释: horse -> rorse (将 'h' 替换为 'r') rorse -> rose (删除 'r') rose -> ros (删除 'e')
输入: word1 = "intention", word2 = "execution" 输出: 5 解释: intention -> inention (删除 't') inention -> enention (将 'i' 替换为 'e') enention -> exention (将 'n' 替换为 'x') exention -> exection (将 'n' 替换为 'c') exection -> execution (插入 'u')
分析:
本题目符合动态规划思想:原问题可以拆分成重叠的子问题进行解决,我们使用动态规划四步法:
1、刻画一个最优解的结构特征
2、递归定义最优解的值
3、计算最优解的值,通常采用自底向上的方法
4、利用计算出的信息构造一个最优解
(引自算法导论)
步骤一:刻画编辑距离的特征
horse -> ros 可由 [horse->rose子问题]->ros 组成,而[horse->rose子问题] 又可由它的子问题[horse->rorse子问题]->rose 组成,于是可构造递推公式 Mn=Mn-1+min(.),也就是说本次变换只跟 [上次已完成的变换] 和 [本次操作] 有关。
步骤二:分析递归解
word1每个字符的下标使用i表示 word2 每个字符下标使用j表示,我们将word1对应word2的字符的每次变换使用二维数组e[][]表示,其中:
e[i][j] = word1[i]转换到word2[j]的最小操作次数
那么操作方式有三种insert插入、delete删除、replace替换,我们只需选择其中最小的操作次数记录下来,即:
e[i][j] = min(insert,delete,replace)
下面分析完整递归解:
e[i][j] =
i ; (当j=0时_①)
j ; (当i=0时_②)
e[i - 1][j - 1] ;(当word1[i]=word2[j]时_③)
min ( e[i][j - 1] + 1 , e[i - 1][j] + 1 , e[i - 1][j - 1] + 1 ); (当word1[i]!=word2[j]_④)
注:
①当j=0时,即由word1的任何字符变换到空时为删除,word1[i]->null,删除次数就是word1的索引 i ;
②当i=0时,即由空变换到word2的任何字符时为插入,null->word2[j],插入次数就是word2的索引 j ;
③当word1[i]=word2[j]时,不做插入,延续上次操作次数;
④当word1[i]!=word2[j]时:
其中第一项insert:从word1[i]->word2[j]的插入操作只需在word2上一次操作基础上增加一次,word1不变;
其中第二项delete:从word1[i]->word2[j]的删除操作只需在word1上一次操作基础上增加一次,word2不变;
其中第三项replace:从word1[i]->word2[j]的修改操作意味着,在两个字符串上一次操作基础之上做一次插入。
public int minDistance(String word1, String word2) {
int[][] e = new int[word1.length() + 1][word2.length() + 1];
for (int i = 1; i <= word1.length(); i++)
e[i][0] = i;
for (int j = 1; j <= word2.length(); j++)
e[0][j] = j;
for (int i = 1; i <= word1.length(); i++) {
for (int j = 1; j <= word2.length(); j++) {
// 因为变换是从空字符串开始的而不是从word1[0]和word2[0]处开始的,所以i,j变换的实际是i-1,j-1处的字符,所以charAt索引要减一
if (word1.charAt(i - 1) == word2.charAt(j - 1))
e[i][j] = e[i - 1][j - 1];
else
e[i][j] = Math.min(e[i][j - 1], Math.min(e[i - 1][j], e[i - 1][j - 1])) + 1;
}
}
return e[word1.length()][word2.length()];
}