[LeetCode] 72. Edit Distance 编辑距离

 

Given two words word1 and word2, find the minimum number of operations required to convert word1 to word2.html

You have the following 3 operations permitted on a word:git

  1. Insert a character
  2. Delete a character
  3. Replace a character

Example 1:github

Input: word1 = "horse", word2 = "ros"
Output: 3
Explanation: 
horse -> rorse (replace 'h' with 'r')
rorse -> rose (remove 'r')
rose -> ros (remove 'e')

Example 2:数组

Input: word1 = "intention", word2 = "execution"
Output: 5
Explanation: 
intention -> inention (remove 't')
inention -> enention (replace 'i' with 'e')
enention -> exention (replace 'n' with 'x')
exention -> exection (replace 'n' with 'c')
exection -> execution (insert 'u')

 

这道题让求从一个字符串转变到另外一个字符串须要的变换步骤,共有三种变换方式,插入一个字符,删除一个字符,和替换一个字符。题目乍眼一看并不难,可是实际上却暗藏玄机,对于两个字符串的比较,通常都会考虑一下用 HashMap 统计字符出现的频率,可是在这道题却不能够这么作,由于字符串的顺序很重要。还有一种比较常见的错误,就是想固然的认为对于长度不一样的两个字符串,长度的差值都是要用插入操做,而后再对应每位字符,不一样的地方用修改操做,可是其实这样可能会多用操做,由于删除操做有时同时能够达到修改的效果。好比题目中的例子1,当把 horse 变为 rorse 以后,以后只要删除第二个r,跟最后一个e,就能够变为 ros。实际上只要三步就完成了,由于删除了某个字母后,原来左右不相连的字母如今就连一块儿了,有可能恰好组成了须要的字符串。因此在比较的时候,要尝试三种操做,由于谁也不知道当前的操做会对后面产生什么样的影响。对于当前比较的两个字符 word1[i] 和 word2[j],若两者相同,一切好说,直接跳到下一个位置。若不相同,有三种处理方法,首先是直接插入一个 word2[j],那么 word2[j] 位置的字符就跳过了,接着比较 word1[i] 和 word2[j+1] 便可。第二个种方法是删除,即将 word1[i] 字符直接删掉,接着比较 word1[i+1] 和 word2[j] 便可。第三种则是将 word1[i] 修改成 word2[j],接着比较 word1[i+1] 和 word[j+1] 便可。分析到这里,就能够直接写出递归的代码,可是很惋惜会 Time Limited Exceed,因此必需要优化时间复杂度,须要去掉大量的重复计算,这里使用记忆数组 memo 来保存计算过的状态,从而能够经过 OJ,注意这里的 insertCnt,deleteCnt,replaceCnt 仅仅是表示当前对应的位置分别采用了插入,删除,和替换操做,总体返回的最小距离,后面位置的仍是会调用递归返回最小的,参见代码以下:post

 

解法一:优化

class Solution {
public:
    int minDistance(string word1, string word2) {
        int m = word1.size(), n = word2.size();
        vector<vector<int>> memo(m, vector<int>(n));
        return helper(word1, 0, word2, 0, memo);
    }
    int helper(string& word1, int i, string& word2, int j, vector<vector<int>>& memo) {
        if (i == word1.size()) return (int)word2.size() - j;
        if (j == word2.size()) return (int)word1.size() - i;
        if (memo[i][j] > 0) return memo[i][j];
        int res = 0;
        if (word1[i] == word2[j]) {
            return helper(word1, i + 1, word2, j + 1, memo);
        } else {
            int insertCnt = helper(word1, i, word2, j + 1, memo);
            int deleteCnt = helper(word1, i + 1, word2, j, memo);
            int replaceCnt = helper(word1, i + 1, word2, j + 1, memo);
            res = min(insertCnt, min(deleteCnt, replaceCnt)) + 1;
        }
        return memo[i][j] = res;
    }
};

 

根据以往的经验,对于字符串相关的题目且求极值的问题,十有八九都是用动态规划 Dynamic Programming 来解,这道题也不例外。其实解法一的递归加记忆数组的方法也能够看做是 DP 的递归写法。这里须要维护一个二维的数组 dp,其大小为 mxn,m和n分别为 word1 和 word2 的长度。dp[i][j] 表示从 word1 的前i个字符转换到 word2 的前j个字符所须要的步骤。先给这个二维数组 dp 的第一行第一列赋值,这个很简单,由于第一行和第一列对应的总有一个字符串是空串,因而转换步骤彻底是另外一个字符串的长度。跟以往的 DP 题目相似,难点仍是在于找出状态转移方程,能够举个例子来看,好比 word1 是 "bbc",word2 是 "abcd",能够获得 dp 数组以下:ui

 

  Ø a b c d
Ø 0 1 2 3 4
b 1 1 1 2 3
b 2 2 1 2 3
c 3 3 2 1 2

 

经过观察能够发现,当 word1[i] == word2[j] 时,dp[i][j] = dp[i - 1][j - 1],其余状况时,dp[i][j] 是其左,左上,上的三个值中的最小值加1,其实这里的左,上,和左上,分别对应的增长,删除,修改操做,具体能够参看法法一种的讲解部分,那么能够获得状态转移方程为:url

dp[i][j] =      /    dp[i - 1][j - 1]                                                                   if word1[i - 1] == word2[j - 1]spa

                  \    min(dp[i - 1][j - 1], min(dp[i - 1][j], dp[i][j - 1])) + 1            elsecode

 

解法二:

class Solution {
public:
    int minDistance(string word1, string word2) {
        int m = word1.size(), n = word2.size();
        vector<vector<int>> dp(m + 1, vector<int>(n + 1));
        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[i - 1] == word2[j - 1]) {
                    dp[i][j] = dp[i - 1][j - 1];
                } else {
                    dp[i][j] = min(dp[i - 1][j - 1], min(dp[i - 1][j], dp[i][j - 1])) + 1;
                }
            }
        }
        return dp[m][n];
    }
};

 

Github 同步地址:

https://github.com/grandyang/leetcode/issues/72

 

相似题目:

One Edit Distance

Delete Operation for Two Strings

Minimum ASCII Delete Sum for Two Strings

 

参考资料:

https://leetcode.com/problems/edit-distance/

https://leetcode.com/problems/edit-distance/discuss/25846/C%2B%2B-O(n)-space-DP

 

LeetCode All in One 题目讲解汇总(持续更新中...)

相关文章
相关标签/搜索