当前位置:   article > 正文

leetcode72. 编辑距离(动态规划-java)_java 编辑距离

java 编辑距离

leetcode72. 编辑距离

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/edit-distance

题目描述

给你两个单词 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’)

提示:
0 <= word1.length, word2.length <= 500
word1 和 word2 由小写英文字母组成

解题思路

如果看到这题,刚开始想不到动态规划的状态转移公式,可以先尝试用暴力递归写出暴力尝试,然后去改写成动态规划,

首先看暴力递归如何写,题目交代了。转化有三个操作,删除替换和插入。还有一个隐藏操作,就是字符相同时。要跳过。这样就是一个完整的操作,这几个操作怎么反应到代码中呢,我们用指针卡住两个单词的起始位置或者终点位置,然后一个一个字符去比较,相等,我们就跳过,不等就替换删除或者插入的操作。

//伪代码框架
if (word1.charAt(r1) == word2.charAt(r2)){
	//啥都不做,跳过,直接去下一个位置操作。
	process(r1 - 1.r2 - 2);
}else {
	//插入,插入后,word2 的指针移动,word1的保持不动
	process(r1,r2 - 1) + 1;
	//删除 ,word1 的指针移动,word2的保持不动
	process(r1 - 1,r2) + 1;
	//替换 word1 和qword2 指针都移动
	process(r1 - 1,r2 - 1) + 1;
	//为甚加1,递归后的可能要加上当前字符
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

根据上面伪代码,我们可以进行写出暴力递归的尝试了。只需要补充上base case,是不是很简单,网上很多资料直接上动态规划,看的头大也看不懂。
我们这题,从尾部开始向前去比较操作,你也可以从头往后比较,区别就是,base case .和指针移动方向步一样,你可以自己改写下。

代码演示


    /**
     * 最小编辑距离
     * @param word1
     * @param word2
     * @return
     */
    public int minDistance(String word1, String word2) {
        if (word1.length() == 0){
            return word2.length();
        }
        if (word2.length() == 0){
            return word1.length();
        }
        if (word1.equals(word2)){
            return 0;
        }
        int n = word1.length();
        int m = word2.length();
        int[][]dp = new int[n][m];
        return process(word1.toCharArray(),n -1,word2.toCharArray(),m - 1,dp);
    }

    /**
     * 暴力递归加上缓存,减少重复计算,不加缓存,提交会超时
     * @param word1
     * @param r1 word1 的指针
     * @param word2
     * @param r2 指针
     * @param dp 缓存表
     * @return
     */
    public int process(char[]word1,int r1,char[]word2,int r2,int[][]dp){
        //base case 同时来到-1 ,说明比较完了.返回0
        if (r1 == -1 && r2 == -1){
            return 0;
        }
        //base case r1 结束了,剩下就需要操作r2 的长度,r2 是下标,计算长度要加1
        if (r1 == -1){
            return r2 + 1;
        }
        //base case r2 结束了,剩下就需要操作r1 的长度,r1 是下标,计算长度要加1
        if (r2 == -1){
            return r1 + 1;
        }
        //缓存里有就直接拿
        if (dp[r1][r2] != 0){
            return dp[r1][r2];
        }
        int res = -1;
        if(word1[r1] == word2[r2]){
            //跳过
            res = process(word1,r1 - 1,word2,r2 - 1,dp);
        }else{
            //插入
            int p1 = process(word1,r1,word2,r2 - 1,dp);
            //删除
            int p2 = process(word1,r1 - 1,word2,r2,dp);
            //替换
            int p3 = process(word1,r1 - 1,word2,r2 - 1,dp);
            //要取最小的情况
            res =  Math.min(Math.min(p1,p2),p3) + 1;
        }
        //答案保存到缓存里
        dp[r1][r2] = res;
        return res;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67

动态规划

看懂了暴力递归,改动态规划应该就很容易了,不过还是加个表格演示下。
更容易明白如何初始化dp 表。

在这里插入图片描述
我们可以把dp表中两边为0位置时,需要操作的长度就是字符的长度,可以直接填上,剩下位置就可以按暴力递归中的依赖关系去填上了。

代码演示

 public int minDistance(String word1, String word2){
        if (word1.length() == 0){
            return word2.length();
        }
        if (word2.length() == 0){
            return word1.length();
        }
        if (word1.equals(word2)){
            return 0;
        }
        int n = word1.length();
        int m = word2.length();
        int[][]dp = new int[n + 1][m + 1];
        //初始化第一列
        for (int i = 1; i <= n ;i++){
            dp[i][0]  = i;
        }
        /初始化第一行
        for (int j = 1;j <= m;j++){
            dp[0][j] = j;
        }
        //递归改成表中拿值得过程
        for (int i = 1; i <= n;i++){
            for (int j = 1; j <= m;j++){
                if(word1.charAt(i - 1) == word2.charAt(j - 1)){
                    dp[i][j] = dp[i - 1][j - 1];
                }else{
                    //插入
                   int p1 = dp[i][j - 1]; 
                   //删除
                   int p2 = dp[i - 1][j];
                   //替换\
                   int p3 = dp[i - 1][j - 1];
                   dp[i][j] = Math.min(Math.min(p1,p2),p3) + 1;
                }
            }
        }
        return dp[n][m];
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

动态规划专题

leetcode1049. 最后一块石头的重量 II

leetcode123. 买卖股票的最佳时机 III

leetcode188. 买卖股票的最佳时机 IV

leetcode312. 戳气球

eetcode787. K 站中转内最便宜的航班

leetcode62. 不同路径

leetcode63. 不同路径 II

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/117763
推荐阅读
相关标签
  

闽ICP备14008679号