題目
給定一個源串S和目標串T,能夠對源串進行如下操作:
1.在給定位置上插入一個字符
2.替換任意字符
3.刪除任意字符
寫一個程序,返回最小操作數,使得對源串進行這些操作后等于目標串。
舉例
ab -->abc :1
ab -->ac :1
good -->god :1
小技巧
刪除和插入都可以理解為添加一個占位符,比如:
S :ahi
T : _hi
即刪除S中的a
S : _ello
T : hello
即在S中插入一個h
dp思路
dp[i][j] 表示S的前i個位置 和 T的前j個位置對齊的最少得分
那么就要尋找該狀態的前一個狀態 以導出遞推關系
dp[i-1][j-1] +same(i,j) 本輪對齊操作(比對)的位置就是S的 i 和 T的 j(認為i 和 j對齊),如果二者不同則same為1(替換操作發生)。 因此前一個狀態是dp[i-1][j-1],i-1 和 j-1 對齊。
dp[i-1][j] +1 刪除S的 i ,相當于T中添加占位符與i對齊 本輪發生對齊操作的只有S 的 i,因此前一個狀態是dp[i-1][j] 。刪除S的 i也可以理解為此時只有S的 i是多余的,也就是S的前i-1 和 T 的前 j是對齊的。
dp[i][j-1] +1 S中插入占位符與T 的 j對齊,插入操作。 本輪發生對齊操作的只有T 的 j,因此前一個狀態是dp[i][j-1] 。也可以理解為此時只有T的 j是多余的,也就是S的前 i 和 T 的前 j-1是對齊的。
綜上:
dp[i][j] = min(dp[i-1][j-1] +same(i,j),dp[i-1][j] +1,dp[i][j-1] +1)
初值:
Dp[0][j] = j S 是空的 T有幾位 就插入幾位
Dp[i][0] = i T 是空的 S有幾位 就刪除幾位
時空復雜度:
依次填充二維矩陣嘛 O(m*n) ,m,n是各自長度
代碼
int minDistance(string word1, string word2) { int m = word1.size(); int n = word2.size(); vector<vector<int>> dp(m+1,vector<int>(n+1));//開二維數組 注意加一 for(int i=0;i<=m;++i){ for(int j=0;j<=n;++j){ if(i==0){ dp[i][j] = j; } else if(j==0){ dp[i][j] = i; } else{ //dp數組中的下標k 對應于字符串word中的下標k-1 dp[i][j] = min(dp[i-1][j-1]+((word1[i-1]==word2[j-1])?0:1),min(dp[i][j-1]+1,dp[i-1][j]+1)); } } } return dp[m][n]; }
空間優化:節省一個維度
二維 變一維:
Dp[i][j-1] -> dp[j-1]
Dp[i-1][j] -> dp[j]
int minDistance(string word1, string word2) { int m = word1.size(); int n = word2.size(); vector<int> dp(n+1); for(int i=0;i<=m;++i){ int last; for(int j=0;j<=n;++j){ if(i==0){ dp[j] = j; } else if(j==0){ last = dp[j]; dp[j] = i; //dp[i][0] 因此last:dp[i-1][0] 當下一輪j變成1時使用該last } else{ int temp = dp[j]; //更新之后的dp[j]就是原來的dp[i][j] 未更新的dp[j]就是dp[i-1][j] dp[j-1]就是dp[i][j-1] //last 代替dp[i-1][j-1] 通過dp[j](原dp[i-1][j])延時一步得到 dp[j] = min(last+((word1[i-1]==word2[j-1])?0:1),min(dp[j-1]+1,dp[j]+1)); last = temp; } } } return dp[n]; }