动态规划经典——编辑距离

题目

给你两个单词 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];
    }
}

执行结果

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值