动态规划系列之「编辑距离」

72. 编辑距离

给你两个单词 word1word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。

你可以对一个单词进行如下三种操作:

  1. 插入一个字符
  2. 删除一个字符
  3. 替换一个字符

示例 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')

编辑距离算法被数据科学家广泛应用,是用作机器翻译语音识别评价标准的基本算法。

最直观的方法是暴力检查所有可能的编辑方法,取最短的一个。所有可能的编辑方法达到指数级,但我们不需要进行这么多计算,因为我们只需要找到距离最短的序列而不是所有可能的序列。

我们可以对任意一个单词进行三种操作:

插入一个字符;

删除一个字符;

替换一个字符。

题目给定了两个单词,设为 A 和 B,这样我们就能够六种操作方法。

但我们可以发现,如果我们有单词 A 和单词 B:

  • 对单词 A 删除一个字符和对单词 B 插入一个字符是等价的。例如当单词 A 为 doge,单词 B 为 dog 时,我们既可以删除单词 A 的最后一个字符 e,得到相同的 dog,也可以在单词 B 末尾添加一个字符 e,得到相同的 doge;

  • 同理,对单词 B 删除一个字符和对单词 A 插入一个字符也是等价的;

  • 对单词 A 替换一个字符和对单词 B 替换一个字符是等价的。例如当单词 A 为 bat,单词 B 为 cat 时,我们修改单词 A 的第一个字母 b -> c,和修改单词 B 的第一个字母 c -> b 是等价的。

这样一来,我们可以保留一个字符串不变,只是操作另一个字符串。比如保留B,只操作A:

  • 在单词 A 中插入一个字符

  • 删除 A 中的一个字符

  • 修改 A 中的一个字符

如下图,这里给出了最短编辑距离的其中一种编辑方式:

上图最短编辑距离为5。

可以发现操作不只有三个,其实还有第四个操作,就是什么都不要做(skip)。比如当 $s1[i] == s2[j] $ 时,执行 i − − i-- i j − − j-- j

了解上述过程之后,下面就直接用动态规划的思路解题,定义动态规划数组的含义:

dp[i][j] 表示s1[0...i]s2[0...j] 的最小编辑距离。

base case 就是 dp[..][0]dp[0][..],表示当其中一个字符串0时,最小编辑距离肯定取决于不为0的那个字符串的长度,比如 A 为0,B 不为0,那么最短编辑距离有两种选择:

  • 要么将 B 中的字符全部删除变为0
  • 要么向 A 中逐个添加字符使其跟 B 相同

当 $s1[i] != s2[j] $,有三种选择:

  • 在单词 A 中插入一个字符

  • 删除 A 中的一个字符

  • 修改 A 中的一个字符

只不过这三个选择哪一个,取决于选择哪一个使得dp[i][j],所以选择三个中的最小值

Java实现如下

class Solution {
    
    
    public int minDistance(String word1, String word2) {
    
    
        int m = word1.length();
        int n = word2.length();
        int[][] dp = new int[m+1][n+1];

        // base case
        for(int i = 0; i <= m; i++) dp[i][0] = i;
        for(int i = 0; i <= n; i++) dp[0][i] = i;
        
        for(int i = 1; i <= m; i++){
    
    
            for(int j = 1; j <= n; j++){
    
    
                if(word1.charAt(i-1) == word2.charAt(j-1)){
    
    
                    dp[i][j] = dp[i-1][j-1];
                }else{
    
    
                    dp[i][j] = min(
                        dp[i][j-1] + 1,
                        dp[i-1][j] + 1,
                        dp[i-1][j-1] + 1
                    );
                }
            }
        }
        return dp[m][n];
    }

    public static int min(int a, int b, int c){
    
    
        return Math.min(a, Math.min(b, c));
    }
}

时间复杂度:O(n^2)

空间复杂度:O(n^2)

猜你喜欢

转载自blog.csdn.net/weixin_44471490/article/details/109267942
今日推荐