题目
给你两个单词 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')
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/edit-distance
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
分析
1.编辑方式有三种,增删替换,对应一个字符的操作,具体替换哪个一个位置,则需要我们一步一步去遍历。
简化为我们需要对每个字符进行比较,l1表示word1对应的字符的index,l2表示word2对应的index,返回值为我们期望的最小次数。通过递归,遍历所有情况,就可以穷举出结果,方法可以表示为
private int minDistanceLength(int l1, int l2) {}
那么对应的删除(l1左移一位),插入(l2左移一位),替换(l1和l2都左移一位)的递归为:
private int minDistanceLength(int l1, int l2) {}
//
minDistanceLength(l1-1,l2)+1,//删除
minDistanceLength(l1,l2-1)+1),//插入
minDistanceLength(l1-1,l2-1)+1//替换
对特殊情况进行,判定,找到边界判定条件
- 当l1==-1时,l1已经遍历完了,剩余需要执行(l1全部新增)的操作次数就为l2+1,
- 同理,当l2==-1时,l2已经遍历完了,剩余需要执行(l2全部删除)的操作次数就为l1+1,
- 当字符相等的时候,不需要进行比较,直接跳过当前比较
private int minDistanceLength(int l1, int l2) {
if (l1==-1){
return l2+1;
}
if (l2==-1){
return l1+1;
}
if (w1[l1]==w2[l2]){
//一样,跳过
return minDistanceLength(l1-1,l2-1);
}
return Math.min(
Math.min(
minDistanceLength(l1-1,l2)+1,//删除
minDistanceLength(l1,l2-1)+1),//插入
minDistanceLength(l1-1,l2-1)+1//替换
);
}
完整代码如下:
package com.learn.mk.dp;
/*
给你两个单词 word1 和 word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
插入一个字符
删除一个字符
替换一个字符
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/edit-distance
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
*/
public class Leetcode72 {
public static void main(String[] args) {
Leetcode72 l = new Leetcode72();
String word1 = "horse";
String word2 = "ros";
System.out.println(l.minDistance(word1, word2));
}
char[] w1;
char[] w2;
public int minDistance(String word1, String word2) {
w1 = word1.toCharArray();
w2 = word2.toCharArray();
return minDistanceLength(w1.length-1, w2.length-1);
}
private int minDistanceLength(int l1, int l2) {
if (l1==-1){
return l2+1;
}
if (l2==-1){
return l1+1;
}
if (w1[l1]==w2[l2]){
//一样,跳过
return minDistanceLength(l1-1,l2-1);
}
return Math.min(
Math.min(
minDistanceLength(l1-1,l2)+1,//删除
minDistanceLength(l1,l2-1)+1),//插入
minDistanceLength(l1-1,l2-1)+1//替换
);
}
}
执行结果
超时分析
超时的问题,大多可以用备忘录解决,即存在重复计算,需要缓存之前已经计算过的值,避免重复计算过多
那么这里存在哪里的重复计算呢,如这里的先删除再插入(minDistanceLength(l1-1,l2-1))+2,先插入再删除(minDistanceLength(l1-1,l2-1))+2,替换(minDistanceLength(l1-1,l2-1))+1,都重复计算了minDistanceLength(l1-1,l2-1)。
private int minDistanceLength(int l1, int l2) {
//......
return Math.min(
minDistanceLength(l1-1,l2)+1,//删除
minDistanceLength(l1,l2-1)+1),//插入
minDistanceLength(l1-1,l2-1)+1//替换
}
使用备忘录缓存
代码如下,具体不做过多分析,就是把计算过的值存储起来,下次使用的时候先判定是否计算过,如果计算过,直接取结果
package com.learn.mk.dp;
/*
给你两个单词 word1 和 word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
插入一个字符
删除一个字符
替换一个字符
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/edit-distance
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
*/
public class Leetcode72 {
public static void main(String[] args) {
Leetcode72 l = new Leetcode72();
String word1 = "horse";
String word2 = "ros";
System.out.println(l.minDistance(word1, word2));
}
char[] w1;
char[] w2;
int[][] memo;
public int minDistance(String word1, String word2) {
w1 = word1.toCharArray();
w2 = word2.toCharArray();
memo=new int[w1.length][w2.length];
return minDistanceLength(w1.length-1, w2.length-1);
}
private int minDistanceLength(int l1, int l2) {
if (l1==-1){
return l2+1;
}
if (l2==-1){
return l1+1;
}
//如果备忘录中可以找到,return
if (memo[l1][l2]!=0){
return memo[l1][l2];
}
if (w1[l1]==w2[l2]){
memo[l1][l2]=minDistanceLength(l1-1,l2-1);
//一样,跳过
return memo[l1][l2];
}
memo[l1][l2]=Math.min(
Math.min(
minDistanceLength(l1-1,l2)+1,//删除
minDistanceLength(l1,l2-1)+1),//插入
minDistanceLength(l1-1,l2-1)+1//替换
);
return memo[l1][l2];
}
}
执行结果