编辑距离(edit distance)

编辑距离

LeetCode 72. 编辑距离

概念

编辑距离,是指将字符串word1通过替换、删除、增加字符的操作,变成字符串word2的最小次数。

用途

编辑距离可以用在自然语言处理中,例如拼写检查可以根据一个拼错的字和其他正确的字的编辑距离,判断哪一个(或哪几个)是比较可能的字。

DNA也可以用A、C 、G和T组成的字符串表示,因此编辑距离也可以用在生物信息学中,判断两个DNA的相似程度。

Unix下的diff及patch命令也是利用编辑距离来进行文本编辑对比的例子。

动态规划简介

动态规划,是指当前状态是由之前的状态通过转移方程(即某一种或几种方跃迁方式)得到的。也就是说,通过设法利用之前所存储的状态(一般是一维、二维、三维数组等存放状态),来更新现有的状态。最后,依次迭代,获得最终状态。

这种类型题是用了动态规划的做法来做。

题目描述

给定两个单词 word1 和 word2,计算出将 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')

初始化dp(重要)

以示例一为例,对二维数组dp进行初始化。dp数组的大小为dp[word1.size()+1][word2.size()+1]。数组大小原因如下:

  1. 可能要对字符串word1的头部插入1到word2.size()个字符,使其变成word2字符串。这便是带有一行初始化的操作意义。
  2. 可能对字符串word1的头部进行1到word1.size()的删除,伴随其他增、改操作,以其变成word2字符串。这便是带有那一列初始化的操作意义。
0 r o s
0 1 2 3
h 1
o 2
r 3
s 4
e 5

状态转移方程(核心)

当前状态,可由上一个状态A加上增加一个字符、上一个状态B修改(或不修改)一个字符或上一个状态C删除一个字符得到。每个状态包含着该状态最小操作步数。

如:

h o r s e

r o s

中, h与r的编辑操作中,显然是将h修改成r为最小操作数(一个操作数)。而不是将h前面加上r,再删除h(两个操作数)或是删除h,再加上r(两个操作数)。

对应到dp数组中,即dp数组中状态由标有的三个状态通过转移方程而来。

0 * r o s
* 2 3
h
o 2
r 3
s 4
e 5

状态转移方程为:

由上面的讲解。可知,的最小操作数是一,即由编辑操作中的修改操作而来的。也就是说,从状态转移方程的第三个式子而来。

由状态转移方程得到最终dp数组如下所示:

0 r o s
0 1 2 3
h 1 1 2 3
o 2 2 1 2
r 3 2 2 2
s 4 3 3 2
e 5 4 4 3

实现代码

class Solution {
public:
    int minDistance(string word1, string word2) {
        if(word1.empty() || word2.empty()) {
            return max(word1.size(), word2.size());
        }
        
        // 建立dp数组。
        int dp[word1.size()+1][word2.size()+1];
        
        // 初始化dp数组,为删除1到word1.size()个字符。
        for(int i=0; i <= word1.size(); ++i) {
            dp[i][0] = i;
        }
        
        // 初始化dp数组,为增加1到word2.size()个字符。
        for(int i=0; i <= word2.size(); ++i) {
            dp[0][i] = i;
        }
        
        // 进行dp操作,由上述的状态转移方程迁移而来。
        for(int i=1; i <= word1.size(); ++i) {
            for(int j=1; j <= word2.size(); ++j) {
                dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + 1;
                dp[i][j] = min(dp[i][j], dp[i-1][j-1] + (word1[i-1] == word2[j-1] ? 0 : 1));
            }
        }
        
        // 返回dp的最终结果。
        return dp[word1.size()][word2.size()];
    }
};

评测结果:

  • 执行用时 : 20 ms, 在Edit Distance的C++提交中击败了18.77% 的用户

  • 内存消耗 : 9.4 MB, 在Edit Distance的C++提交中击败了4.60% 的用户

代码优化

从dp的角度来说,时间复杂度基本是, 没啥优化空间。

但是,由dp数组的使用上可以观察到,每次dp的时候,只是使用当前行与上一行而已。因此,dp数组只需要开dp[2][word2.size()]大小,复用该数组即可。即空间复杂度从降为.

优化后的代码:

class Solution {
public:
    int minDistance(string word1, string word2) {
        if(word1.empty() || word2.empty()) {
            return max(word1.size(), word2.size());
        }
        
        // 建立dp数组。
        int dp[2][word2.size()+1];
                
        // 初始化dp数组,为增加1到word2.size()个字符。
        for(int i=0; i <= word2.size(); ++i) {
            dp[0][i] = i;
        }
        
        // 进行dp操作,由上述的状态转移方程迁移而来。
        // 增加两个变量,用来标记dp数组的操作方式。
        int last_index = 0;
        int current_index = 1;
        for(int i=1; i <= word1.size(); ++i) {
            // 初始化dp数组,表示删除word字符的操作数
            dp[current_index][0] = i;
            
            // 通过状态方程进行跃迁
            for(int j=1; j <= word2.size(); ++j) {
                dp[current_index][j] = min(dp[last_index][j], dp[current_index][j-1]) + 1;
                dp[current_index][j] = min(dp[current_index][j], dp[last_index][j-1] + (word1[i-1] == word2[j-1] ? 0 : 1));
            }
            
            // 交换这两个变量,复用数组
            swap(last_index, current_index);
        }
        
        // 返回dp的最终结果。
        return dp[last_index][word2.size()];
    }
};

评测结果:

  • 执行用时 : 36 ms, 在Edit Distance的C++提交中击败了10.46% 的用户
  • 内存消耗 : 8.4 MB, 在Edit Distance的C++提交中击败了4.60% 的用户

此次优化,由于是复用dp数组,所以是由原来的变成。但是从评测结果来说,可知内存的优化上变化不是很明显,说明评测数据量并不大。

你可能感兴趣的:(编辑距离(edit distance))