動態規劃——編輯距離
問題來源:leetcode 72。
編輯距離
給你兩個單詞 word1
和 word2
,請你計算出將 word1
轉換成 word2
所使用的最少運算元。
你可以對一個單詞進行如下三種操作:
- 插入一個字元
- 刪除一個字元
- 替換一個字元
示例 1:
輸入:word1 = "horse", word2 = "ros"
輸出:3
解釋:
horse -> rorse (將 'h' 替換為 'r')
rorse -> rose (刪除 'r')
rose -> ros (刪除 'e')
示例 2:
輸入:word1 = "intention", word2 = "execution" 輸出:5 解釋: intention -> inention (刪除 't') inention -> enention (將 'i' 替換為 'e') enention -> exention (將 'n' 替換為 'x') exention -> exection (將 'n' 替換為 'c') exection -> execution (插入 'u')
動態規劃
分析過程與最長公共子序列非常像。
設字串 X n = < x 1 , . . . , x n > X_n=<x_1,...,x_n> Xn=<x1,...,xn> 與 Y m = < y 1 , . . . , n m > Y_m=<y_1,...,n_m> Ym=<y1,...,nm> 的編輯距離為 E D ( X n , Y m ) ED(X_n,Y_m) ED(Xn,Ym)。
優化子結構:
- 如果
<
x
n
=
y
m
>
<x_n=y_m>
<xn
- 如果
<
x
n
≠
y
m
>
<x_n \neq y_m>
- 可以將 x n x_n xn 替換成 y m y_m ym,然後將 X n − 1 X_{n-1} Xn−1 轉換成 Y m − 1 Y_{m-1} Ym−1,即需要求解子問題 E D ( X n − 1 , Y m − 1 ) ED(X_{n-1},Y_{m-1}) ED(Xn−1,Ym−1)。
- 可以將 x n x_n xn 刪除,然後將 X n − 1 X_{n-1} Xn−1 轉換成 Y m Y_m Ym,即需要求解子問題 E D ( X n − 1 , Y m ) ED(X_{n-1},Y_m) ED(Xn−1,Ym)。
- 可以在 X n X_n Xn 末尾新增 y m y_m ym,然後將 X n X_n Xn 轉換成 Y m − 1 Y_{m-1} Ym−1,即需要求解子問題 E D ( X n , Y m − 1 ) ED(X_n,Y_{m-1}) ED(Xn,Ym−1)。
上面分析結果表明,無論何種情況,原問題的最優解均可以由某些子問題的解構造得到,即編輯距離具有優化子結構。
重疊子問題:計算 E D ( X n , Y m ) ED(X_n,Y_m) ED(Xn,Ym) 時需要計算 E D ( X n − 1 , Y m − 1 ) ED(X_{n-1},Y_{m-1}) ED(Xn−1,Ym−1)、 E D ( X n − 1 , Y m ) ED(X_{n-1},Y_m) ED(Xn−1,Ym) 以及 E D ( X n , Y m − 1 ) ED(X_{n},Y_{m-1}) ED(Xn,Ym−1),而計算 E D ( X n − 1 , Y m ) ED(X_{n-1},Y_m) ED(Xn−1,Ym) 和 E D ( X n , Y m − 1 ) ED(X_{n},Y_{m-1}) ED(Xn,Ym−1) 時都需要計算 E D ( X n − 1 , Y m − 1 ) ED(X_{n-1},Y_{m-1}) ED(Xn−1,Ym−1),因此具有重疊子問題。
遞迴地定義最優解的值:設 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示字串 X i = < x 1 , . . . , x i > X_i=<x_1,...,x_i> Xi=<x1,...,xi> 和 Y j = < y 1 , . . . , y j > Y_j=<y_1,...,y_j> Yj=<y1,...,yj> 的編輯距離:
d p [ i ] [ j ] = { i i f j = 0 j i f i = 0 d p [ i ] [ j ] i f x i = y j 1 + m i n { d p [ i − 1 ] [ j ] , d p [ i ] [ j − 1 ] , d p [ i − 1 ] [ j − 1 ] } i f x i ≠ y j dp[i][j]=\left\{\begin{array}{cc} i & if\ j=0 \\ j & if\ i=0 \\ dp[i][j] & if\ x_i=y_j \\ 1+min\{dp[i-1][j],dp[i][j-1],dp[i-1][j-1]\} & if\ x_i \neq y_j \end{array}\right. dp[i][j]=⎩⎪⎪⎨⎪⎪⎧ijdp[i][j]1+min{dp[i−1][j],dp[i][j−1],dp[i−1][j−1]}ifj=0ifi=0ifxi=yjifxi=yj
自底向上地計算最優解的值:首先填充邊界值,然後採用逐行計算、逐列計算 d p dp dp 陣列的值,可以保證在計算每一個 d p [ i ] [ j ] dp[i][j] dp[i][j] 時,相關的子問題結果均已經被計算過且儲存在了表格中。
class Solution {
public:
int minDistance(string word1, string word2) {
int n = word1.size(), m = word2.size();
if(n == 0 || m == 0) {
return max(n, m);
}
vector<vector<int>> dp(n+1, vector<int>(m+1));
for(int i=0; i<=n; i++) {
dp[i][0] = i;
}
for(int j=0; j<=m; j++) {
dp[0][j] = j;
}
for(int i=1; i<=n; i++) {
for(int j=1; j<=m; j++) {
if(word1[i-1] == word2[j-1]) {
dp[i][j] = dp[i-1][j-1];
} else {
dp[i][j] = min(min(dp[i-1][j], dp[i][j-1]), dp[i-1][j-1]) + 1;
}
}
}
return dp[n][m];
}
};