zl程序教程

您现在的位置是:首页 >  其他

当前栏目

【LeetCode】编辑距离 [H](动态规划)

LeetCode规划 动态 编辑 距离
2023-09-27 14:19:52 时间

72. 编辑距离 - 力扣(LeetCode)

一、题目

给你两个单词 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 由小写英文字母组成

二、代码

class Solution {
    public int minDistance(String word1, String word2) {
        if (word1 == null || word2 == null) {
            return 0;
        }

        char[] s1 = word1.toCharArray();
        char[] s2 = word2.toCharArray();

        // dp[i][j]:s1前缀取前i个字符,编辑成s2前缀取j个字符,将s1的前i个字符组成的前缀串如何用最少编辑代价转换成s2的前j个字符的前缀串,最少代价是多少。
        // dp[i][j] = -1表示当前状态下无法编辑成功
        int[][] dp = new int[s1.length + 1][s2.length + 1];

        // 初始化dp数组  规定dp[0][0] = 0   
        // 0列s2编辑成s1空串,只能删除
        for (int i = 1; i <= s1.length; i++) {
            dp[i][0] = 1 * i;
        }
        // 0行s1空串编辑为s2,只能添加
        for (int j = 1; j <= s2.length; j++) {
            dp[0][j] = 1 * j;
        }

        for (int i = 1; i <= s1.length; i++) {
            for (int j = 1; j <= s2.length; j++) {
                // 分情况讨论
                // 可能性1:s1的前i-1个字符可以变成s2的前j个字符,即删掉s1的最后一个i位置的字符即可完成编辑,如果dp[i - 1][j] == -1,说明无法编辑成功,赋值为-1
                int p1 = dp[i - 1][j] != -1 ? dp[i - 1][j] + 1 : -1;
                // 可能性2:s1的前i个字符先变成str2的前j-1个字符,后再加上s2的最后一个j位置的字符即可完成编辑。如果dp[i][j - 1] == -1,说明无法编辑成功,赋值为-1
                int p2 = dp[i][j - 1] != -1 ? dp[i][j - 1] + 1 : -1;
                // 可能性3: s1, s2两个字符串最后一个字符串相等,如果str1前面i-1个字符可以编辑成s2前面j-1个字符,那么最后一个字符保留即可完成编辑。
                // 可能性4:s1, s2两个字符串最后一个字符串不相等,如果str1前面i-1个字符可以编辑成s2前面j-1个字符,那么最后一个字符替换即可完成编辑。
                int p3 = s1[i - 1] == s2[j - 1] ? (dp[i - 1][j - 1] != -1 ? dp[i - 1][j - 1] : -1) : (dp[i - 1][j - 1] != -1 ? dp[i - 1][j - 1] + 1 : -1);

                // 取四种情况的最小值
                dp[i][j] = Math.min(p1, Math.min(p2, p3));
            }
        }
        // 返回答案
        return dp[s1.length][s2.length];
    }
}

三、解题思路 

道题最大的难点是将所有的可能性都想出来,不能有遗漏。一共有四种可能:

  • 可能性1:s1的前i-1个字符可以变成s2的前j个字符,即删掉s1的最后一个i位置的字符即可完成编辑,如果dp[i - 1][j] == -1,说明无法编辑成功,赋值为-1
  • 可能性2:s1的前i个字符先变成str2的前j-1个字符,后再加上s2的最后一个j位置的字符即可完成编辑。如果dp[i][j - 1] == -1,说明无法编辑成功,赋值为-1
  • 可能性3: s1, s2两个字符串最后一个字符串相等,如果str1前面i-1个字符可以编辑成s2前面j-1个字符,那么最后一个字符保留即可完成编辑。
  • 可能性4:s1, s2两个字符串最后一个字符串不相等,如果str1前面i-1个字符可以编辑成s2前面j-1个字符,那么最后一个字符替换即可完成编辑。