題目
兩個單詞 word1 和 word2,可以對一個單詞進行插入一個字符、刪除一個字符、替換一個字符三種操作,計算將 word1 轉換成 word2 所使用的最少操作數 。
思路
解決兩個字符串的動態規劃問題,一般都是用兩個指針i,j分別指向兩個字符串的最後,再一步步往前縮小問題的規模
那麼針對這題的具體思路:
i, j 分別放置在word1, word2上,i, j就是狀態,對狀態的選擇一共有四種:相等skip不操作, 不相等插入,或刪除,或替換,我們在後三種選擇中選擇以此產生的最小編輯距離
插入: 我們要考慮完成這個動作之後的變化,新元素是是插入在i後面的,所以當下比較是新元素和j的位置,那操作之後j就需要往前一個了,而i可以不用變
刪除: 要將現在i的位置元素刪除,這個j所指元素要和i-1去合更好,所以i-1, j
替換: 替換成功了,都往前走一步i-1, j-1
dp[i][j] = min(dp[i, j-1]+1, dp[i-1, j]+1, dp[i-1][j-1]+1)
base case就是一個串已經走完了,就直接返回另一個串的剩餘字符即可
很明顯這會有大量的重複操作,但還是先寫寫直接的吧,看看會不會超時報錯
暴力思路dp代碼
class Solution {
public:
int dp(int i , int j, const string &word1, const string &word2)
{
if(i == -1) return j+1;
if(j == -1) return i+1;
if(word1[i] == word2[j])//相等就skip
{
return dp(i-1, j-1, word1, word2);
}else{
return min(min(dp(i, j-1, word1, word2), dp(i-1,j, word1, word2)), dp(i-1, j-1, word1, word2))+1;
}
}
int minDistance(string word1, string word2) {
int m = word1.size();
int n = word2.size();
return dp(m-1, n-1, word1, word2);
}
};
果然超時,加個備忘錄吧
帶備忘錄代碼
注意vector類型memo的邊界處理,容易報錯,不要直接傳遞元素索引了😟
class Solution {
public:
int dp(int i , int j, const string &word1, const string &word2, vector<vector<int>>& memo)
{
if(memo[i][j] != -1) return memo[i][j];
if(i == 0) return j;
if(j == 0) return i;
if(word1[i-1] == word2[j-1])//相等就skip
{
memo[i][j] = dp(i-1, j-1, word1, word2, memo);
}else{
memo[i][j] = min(min(dp(i, j-1, word1, word2,memo), dp(i-1,j, word1, word2,memo)), dp(i-1, j-1, word1, word2, memo))+1;
}
return memo[i][j];
}
int minDistance(string word1, string word2) {
int m = word1.size();
int n = word2.size();
vector<vector<int>> memo(m+1, vector<int>(n+1, -1));
return dp(m, n, word1, word2, memo);
}
};