[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 題目講解彙總(持續更新中...)

相關文章
相關標籤/搜索