Leetcode72 编辑距离 C++,Java,Python

Leetcode72 编辑距离

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

博主Github:https://github.com/GDUT-Rp/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')

解题思路:

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

    最简单的方法是检查所有可能的编辑序列,从中找出最短的一条。但这个序列总数可能达到指数级,但完全不需要这么多,因为我们只要找到距离最短的那条而不是所有可能的序列。

方法一:动态规划

我们的目的是让问题简单化,比如说两个单词 horseros 计算他们之间的编辑距离 D,容易发现,如果把单词变短会让这个问题变得简单,很自然的想到用 D[n][m] 表示输入单词长度为 nm 的编辑距离。

具体来说,D[i][j] 表示 word1 的前 i 个字母和 word2 的前 j 个字母之间的编辑距离。

Leetcode72 编辑距离 C++,Java,Python_第1张图片

当我们获得 D[i-1][j]D[i][j-1]D[i-1][j-1] 的值之后就可以计算出 D[i][j]

每次只可以往单个或者两个字符串中插入一个字符

dp[i][j] 代表 word1i 位置转换成 word2j 位置需要最少步数

所以,

当 word1[i] == word2[j],dp[i][j] = dp[i-1][j-1]

当 word1[i] != word2[j],dp[i][j] = min(dp[i-1][j-1], dp[i-1][j], dp[i][j-1]) + 1

其中,dp[i-1][j-1] 表示替换操作,dp[i-1][j] 表示删除操作,dp[i][j-1] 表示插入操作。

那么递推公式很显然了

如果两个子串的最后一个字母相同,word1[i] = word2[i] 的情况下:

D [ i ] [ j ] = 1 + min ⁡ ( D [ i − 1 ] [ j ] , D [ i ] [ j − 1 ] , D [ i − 1 ] [ j − 1 ] − 1 ) D[i][j] = 1 + \min(D[i - 1][j], D[i][j - 1], D[i - 1][j - 1] - 1) D[i][j]=1+min(D[i1][j],D[i][j1],D[i1][j1]1)
否则,word1[i] != word2[i] 我们将考虑替换最后一个字符使得他们相同:

D [ i ] [ j ] = 1 + m i n ( D [ i − 1 ] [ j ] , D [ i ] [ j − 1 ] , D [ i − 1 ] [ j − 1 ] ) D[i][j]=1+min(D[i−1][j],D[i][j−1],D[i−1][j−1]) D[i][j]=1+min(D[i1][j],D[i][j1],D[i1][j1])
所以每一步结果都将基于上一步的计算结果,示意如下:

Leetcode72 编辑距离 C++,Java,Python_第2张图片

同时,对于边界情况,一个空串和一个非空串的编辑距离为 D[i][0] = iD[0][j] = j

综上我们得到了算法的全部流程。

Leetcode72 编辑距离 C++,Java,Python_第3张图片

Leetcode72 编辑距离 C++,Java,Python_第4张图片

Leetcode72 编辑距离 C++,Java,Python_第5张图片

Leetcode72 编辑距离 C++,Java,Python_第6张图片

Leetcode72 编辑距离 C++,Java,Python_第7张图片

Leetcode72 编辑距离 C++,Java,Python_第8张图片

C++:

#include 

using namespace std;

class Solution_LeetCode72 {
public:
    int minDistance(string word1, string word2) {
        int n = word1.size();
        int m = word2.size();

        if (n * m == 0) {
            return n + m;
        }

        int d[n + 1][m + 1];
        for (int i = 0; i < n + 1; ++i) {
            d[i][0] = i;
        }
        for (int i = 0; i < m + 1; ++i) {
            d[0][i] = i;
        }

        for (int i = 1; i < n + 1; ++i) {
            for (int j = 1; j < m + 1; ++j) {
                int left = d[i - 1][j] + 1;
                int down = d[i][j - 1] + 1;
                int left_down = d[i - 1][j - 1];
                if (word1[i - 1] != word2[j - 1]) {
                    left_down += 1;
                }
                d[i][j] = min(left, min(down, left_down));
            }
        }
        return d[n][m];
    }
};

Java:

class Solution {
  public int minDistance(String word1, String word2) {
    int n = word1.length();
    int m = word2.length();

    // if one of the strings is empty
    if (n * m == 0)
      return n + m;

    // array to store the convertion history
    int [][] d = new int[n + 1][m + 1];

    // init boundaries
    for (int i = 0; i < n + 1; i++) {
      d[i][0] = i;
    }
    for (int j = 0; j < m + 1; j++) {
      d[0][j] = j;
    }

    // DP compute 
    for (int i = 1; i < n + 1; i++) {
      for (int j = 1; j < m + 1; j++) {
        int left = d[i - 1][j] + 1;
        int down = d[i][j - 1] + 1;
        int left_down = d[i - 1][j - 1];
        if (word1.charAt(i - 1) != word2.charAt(j - 1))
          left_down += 1;
        d[i][j] = Math.min(left, Math.min(down, left_down));

      }
    }
    return d[n][m];
  }
}

Python:

class Solution:
    def minDistance(self, word1, word2):
        """
        :type word1: str
        :type word2: str
        :rtype: int
        """
        n = len(word1)
        m = len(word2)
        
        # if one of the strings is empty
        if n * m == 0:
            return n + m
        
        # array to store the convertion history
        d = [ [0] * (m + 1) for _ in range(n + 1)]
        
        # init boundaries
        for i in range(n + 1):
            d[i][0] = i
        for j in range(m + 1):
            d[0][j] = j
        
        # DP compute 
        for i in range(1, n + 1):
            for j in range(1, m + 1):
                left = d[i - 1][j] + 1
                down = d[i][j - 1] + 1
                left_down = d[i - 1][j - 1] 
                if word1[i - 1] != word2[j - 1]:
                    left_down += 1
                d[i][j] = min(left, down, left_down)
        
        return d[n][m]

复杂度分析

  • 时间复杂度 : O ( m n ) O(mn) O(mn),两层循环显而易见。
  • 空间复杂度 : O ( m n ) O(mn) O(mn),循环的每一步都要记录结果。

你可能感兴趣的:(Leetcode)