[LeetCode] Delete Operation for Two Strings 兩個字符串的刪除操作


 

Given two words word1 and word2, find the minimum number of steps required to make word1 and word2 the same, where in each step you can delete one character in either string.

Example 1:

Input: "sea", "eat"
Output: 2
Explanation: You need one step to make "sea" to "ea" and another step to make "eat" to "ea".

 

Note:

  1. The length of given words won't exceed 500.
  2. Characters in given words can only be lower-case letters.

 

這道題給了我們兩個單詞,問我們最少需要多少步可以讓兩個單詞相等,每一步我們可以在任意一個單詞中刪掉一個字符。那么我們分析怎么能讓步數最少呢,是不是知道兩個單詞最長的相同子序列的長度,並乘以2,被兩個單詞的長度之和減,就是最少步數了。其實這道題就轉換成求Longest Common Subsequence最長相同子序列的問題,令博主意外的是,LeetCode中竟然沒有這道題,這與包含萬物的LeetCode的作風不符啊。不過沒事,有這道題也行啊,對於這種玩字符串,並且是求極值的問題,十有八九都是用dp來解的,曾經有網友問博主,如何確定什么時候用greedy,什么時候用dp?其實博主也不不太清楚,感覺dp要更tricky一些,而且出現的概率大,所以博主一般會先考慮dp,如果實在想不出遞推公式,那么就想想greedy能做不。如果有大神知道更好的區分方法,請一定留言告知博主啊,多謝!那么決定了用dp來做,就定義一個二維的dp數組,其中dp[i][j]表示word1的前i個字符和word2的前j個字符組成的兩個單詞的最長公共子序列的長度。下面來看遞推式dp[i][j]怎么求,首先來考慮dp[i][j]和dp[i-1][j-1]之間的關系,我們可以發現,如果當前的兩個字符相等,那么dp[i][j] = dp[i-1][j-1] + 1,這不難理解吧,因為最長相同子序列又多了一個相同的字符,所以長度加1。由於我們dp數組的大小定義的是(n1+1) x (n2+1),所以我們比較的是word1[i-1]和word2[j-1]。那么我們想如果這兩個字符不相等呢,難道我們直接將dp[i-1][j-1]賦值給dp[i][j]嗎,當然不是,我們還要錯位相比嘛,比如就拿題目中的例子來說,"sea"和"eat",當我們比較第一個字符,發現's'和'e'不相等,下一步就要錯位比較啊,比較sea中第一個's'和eat中的'a',sea中的'e'跟eat中的第一個'e'相比,這樣我們的dp[i][j]就要取dp[i-1][j]跟dp[i][j-1]中的較大值了,最后我們求出了最大共同子序列的長度,就能直接算出最小步數了,參見代碼如下:

 

解法一:

class Solution {
public:
    int minDistance(string word1, string word2) {
        int n1 = word1.size(), n2 = word2.size();
        vector<vector<int>> dp(n1 + 1, vector<int>(n2 + 1, 0));
        for (int i = 1; i <= n1; ++i) {
            for (int j = 1; j <= n2; ++j) {
                if (word1[i - 1] == word2[j - 1]) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                } else {
                    dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
                }
            }
        }
        return n1 + n2 - 2 * dp[n1][n2];
    }
};

 

下面這種方法也是用的dp,但是和上面的dp思路不太一樣,這種算法是跟之前那道Edit Distance相同的思路。那道題問我們一個單詞通過多少步修改可以得到另一個單詞,其實word2刪除一個字符,和跟在word1對應的地方加上那個要刪除的字符,達到的效果是一樣的,並不影響最終的步驟數,所以這道題完全可以按照那道題的解法來做,一點都不需要變動,定義一個二維的dp數組,其中dp[i][j]表示word1的前i個字符和word2的前j個字符組成的兩個單詞,能使其變相同的最小的步數,講解可以參看那篇帖子,參見代碼入下:

 

解法二:

class Solution {
public:
    int minDistance(string word1, string word2) {
        int n1 = word1.size(), n2 = word2.size();
        vector<vector<int>> dp(n1 + 1, vector<int>(n2 + 1, 0));
        for (int i = 0; i <= n1; ++i) dp[i][0] = i;
        for (int j = 0; j <= n2; ++j) dp[0][j] = j;
        for (int i = 1; i <= n1; ++i) {
            for (int j = 1; j <= n2; ++j) {
                if (word1[i - 1] == word2[j - 1]) {
                    dp[i][j] = dp[i - 1][j - 1];
                } else {
                    dp[i][j] = 1 + min(dp[i - 1][j], dp[i][j - 1]);
                }
            }
        }
        return dp[n1][n2];
    }
};

 

下面這種方法是解法二的遞歸寫法,用的優化的dfs的方法,用memo數組來保存中間計算結果,以避免大量的重復計算,參見代碼如下:

 

解法三:

class Solution {
public:
    int minDistance(string word1, string word2) {
        int n1 = word1.size(), n2 = word2.size();
        vector<vector<int>> memo(n1 + 1, vector<int>(n2 + 1, 0));
        return helper(word1, word2, 0, 0, memo);
    }
    int helper(string word1, string word2, int p1, int p2, vector<vector<int>>& memo) {
        if (memo[p1][p2] != 0) return memo[p1][p2];
        int n1 = word1.size(), n2 = word2.size();
        if (p1 == n1 || p2 == n2) return n1 - p1 + n2 - p2;
        if (word1[p1] == word2[p2]) {
            memo[p1][p2] = helper(word1, word2, p1 + 1, p2 + 1, memo);
        } else {
            memo[p1][p2] = 1 + min(helper(word1, word2, p1 + 1, p2, memo), helper(word1, word2, p1, p2 + 1, memo));
        }
        return memo[p1][p2];
    }
};

 

類似題目:

Edit Distance

Minimum ASCII Delete Sum for Two Strings

 

參考資料:

https://discuss.leetcode.com/topic/89285/java-dp-solution-longest-common-subsequence

https://discuss.leetcode.com/topic/89308/dynamic-programming-and-memoization-solution

https://discuss.leetcode.com/topic/89433/two-pointers-recursive-java-solution-with-memoization

 

LeetCode All in One 題目講解匯總(持續更新中...)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM