72. 编辑距离

题目链接:力扣

72. 编辑距离_第1张图片 解题思路:

方法一:递归+记忆化搜索

  1. 定义递归函数:minEdits(char[] word1, char[] word2, int pos1, int pos2)
    1. 含义:返回将word1[pos1, ...  , word1.length-1]区间内的字符,转为成word2[pos2, ... , word2.length-1]区间内的字符所使用的最少操作数(索引从0开始)
  2. 递归终止的条件:
    1. 如果pos1==word1.length && pos2==word2.length:说明所有字符已经转换完成,无需任何转换了,return 0
    2. 如果pos1 == word1.length && pos2 < word2.length:
      1. 此时表明 word1[0,word1.length-1] 区间内的字符通过插入、删除、替换三种操作后,已经完全和 word2[0,pos1-1] 完全相等。但是word1 后面已经没有单词了,也就是说word2还有word2.length - pos2个单词没有完成转换,可以插入 word2.length - pos2个单词
      2. return word2.length - pos2。
    3. 如果pos1< word1.lengh && pos2 == word2.length:
      1. 此时表明 word1[0,pos1-1] 区间内的字符通过插入、删除、替换三种操作后,已经完全和 word2[0,word2.length-1] 完全相等,但是word1后面还有多余的字符,这个时候只能删除剩余的单词
      2. return word1.length - pos1
  3. 其他递归状态的转移:
    1. 如果 word1[pos1] == word2[pos2]:
      1. 此时表明当前两个字符已经相等,无需进行转换
      2. return minEdits(word1, word2, pos1+1, pos2+1)
    2. 否则,说明当前两个字符不相等,需要进行转换:
      1. 可以在word1[pos1]的位置插入一个和 word2[pos2]相等的字符。最小操作数为 num1 = minEdits(word1, word2, pos1, pos2+1) +1
      2. 或者可以删除word1[pos1]这个和word2[pos2]不相等的字符,最小操作数为num2= minEdits(word1, word2, pos1+1, pos2) +1
      3. 或者可以将word1[pos1]替换成和word2[pos2]相等的字符,此时最小操作数为num3= minEdits(word1, word2, pos1, pos2+1) +1
      4. 返回三者的最小值
  4. 因为递归操作会有许多重复的过程,可以使用一个二维数组,保存每次递归返回的结果,进入递归时,如果当前递归已经计算过了,就直接返回数组中保存的值。减少重复的递归操作

AC代码:

class Solution {
    //保存递归的结果
    static int[][] dp;

    public static int minDistance(String word1, String word2) {
        dp = new int[word1.length() + 1][word2.length() + 1];
        for (int[] line : dp) {
            Arrays.fill(line, -1);
        }
        return minEdits(word1.toCharArray(), word2.toCharArray(), 0, 0);
    }
    
    public static int minEdits(char[] word1, char[] word2, int pos1, int pos2) {
        //记忆化搜索
        if (dp[pos1][pos2] != -1) {
            return dp[pos1][pos2];
        }
        //说明所有字符已经转换完成,无需任何转换了
        if (pos1 == word1.length && pos2 == word2.length) {
            dp[pos1][pos2] = 0;
            return dp[pos1][pos2];
        }

        //word1[0,word1.length-1] 区间内的字符通过插入、删除、替换三种操作后,已经完全和 word2[0,pos1-1] 完全相等
        if (pos1 == word1.length && pos2 < word2.length) {
            //只能插入
            dp[pos1][pos2] = word2.length-pos2;
            return dp[pos1][pos2];
        }
        //word1[0,pos1-1] 区间内的字符通过插入、删除、替换三种操作后,已经完全和 word2[0,word2.length-1] 完全相等
        if (pos1 < word1.length && pos2 == word2.length) {
            //只能删除
            dp[pos1][pos2] = word1.length-pos1;
            return dp[pos1][pos2];
        }
        //当前位置两个相等,无需操作
        if (word1[pos1] == word2[pos2]) {
            dp[pos1][pos2] = minEdits(word1, word2, pos1 + 1, pos2 + 1);
            return dp[pos1][pos2];
        }

        //当前两个位置的字符不相等

        //在word1[pos1]的位置插入一个和 word2[pos2]相等的字符
        int num1 = minEdits(word1, word2, pos1, pos2 + 1) + 1;
        //删除word1[pos1]这个和word2[pos2]不相等的字符
        int num2 = minEdits(word1, word2, pos1 + 1, pos2) + 1;
        //将word1[pos1]替换成和word2[pos2]相等的字符
        int num3 = minEdits(word1, word2, pos1 + 1, pos2 + 1) + 1;

        dp[pos1][pos2] = Math.min(Math.min(num1, num2), num3);
        return dp[pos1][pos2];
    }
}

72. 编辑距离_第2张图片

方法二:动态规划

可以根据上述递归的过程转化为动态规划的解法

AC代码:

class Solution {
    public static int minDistance(String word1, String word2) {
        int[][] dp = new int[word1.length() + 1][word2.length() + 1];
        dp[word1.length()][word2.length()] = 0;
        //word1[0,word1.length-1] 区间内的字符通过插入、删除、替换三种操作后,已经完全和 word2[0,pos1-1] 完全相等
        for (int i = 0; i < word2.length(); i++) {
            //只能插入
            dp[word1.length()][i] = word2.length() - i;
        }
        //word1[0,pos1-1] 区间内的字符通过插入、删除、替换三种操作后,已经完全和 word2[0,word2.length-1] 完全相等
        for (int i = 0; i < word1.length(); i++) {
            //只能删除
            dp[i][word2.length()] = word1.length() - i;
        }

        for (int i = word1.length() - 1; i >= 0; i--) {
            for (int j = word2.length() - 1; j >= 0; j--) {
                if (word1.charAt(i) == word2.charAt(j)) {
                    dp[i][j] = dp[i + 1][j + 1];
                } else {
                    dp[i][j] = Math.min(Math.min(dp[i][j + 1], dp[i + 1][j]), dp[i + 1][j + 1]) + 1;
                }
            }
        }
        return dp[0][0];
    }
}

72. 编辑距离_第3张图片

通过递归-->记忆化搜索-->动态规划。这种步骤求解方式,可以在不容易推导出dp状态转移方程的时候进行动态规划求解,通过递归一步一步的尝试,最终写出动态规划的解法,整个求解过程中并没有对dp状态转移方程进行推导

其实在递归解法中,从当前递归方程进入会进入到那一层的递归,就是当前dp的状态转移过程,递归的终止条件就是dp数组的初始状态

你可能感兴趣的:(LeetCode_Java版,动态规划,递归,算法)