1. 程式人生 > 其它 >動態規劃——編輯距離

動態規劃——編輯距離

技術標籤:動態規劃演算法動態規劃

問題來源:leetcode 72

編輯距離

給你兩個單詞 word1word2,請你計算出將 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
    =
    ym>
    ,那麼 E D ( X n , Y m ) ED(X_n,Y_m) ED(Xn,Ym) 是子問題 X n − 1 X_{n-1} Xn1 Y m − 1 Y_{m-1} Ym1 的編輯距離,即 E D ( X n , Y m ) = E D ( X n − 1 , Y m − 1 ) ED(X_n,Y_m)=ED(X_{n-1},Y_{m-1}) ED(Xn,Ym)=ED(Xn1,Ym1),也就是說需要求解子問題 E D ( X n − 1 , Y m − 1 ) ED(X_{n-1},Y_{m-1}) ED(Xn1,Ym1)
  • 如果 < x n ≠ y m > <x_n \neq y_m>
    <xn=ym>
    ,那麼:
    1. 可以將 x n x_n xn 替換成 y m y_m ym,然後將 X n − 1 X_{n-1} Xn1 轉換成 Y m − 1 Y_{m-1} Ym1,即需要求解子問題 E D ( X n − 1 , Y m − 1 ) ED(X_{n-1},Y_{m-1}) ED(Xn1,Ym1)
    2. 可以將 x n x_n xn 刪除,然後將 X n − 1 X_{n-1} Xn1 轉換成 Y m Y_m Ym,即需要求解子問題 E D ( X n − 1 , Y m ) ED(X_{n-1},Y_m) ED(Xn1,Ym)
    3. 可以在 X n X_n Xn 末尾新增 y m y_m ym,然後將 X n X_n Xn 轉換成 Y m − 1 Y_{m-1} Ym1,即需要求解子問題 E D ( X n , Y m − 1 ) ED(X_n,Y_{m-1}) ED(Xn,Ym1)

上面分析結果表明,無論何種情況,原問題的最優解均可以由某些子問題的解構造得到,即編輯距離具有優化子結構。

重疊子問題:計算 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(Xn1,Ym1) E D ( X n − 1 , Y m ) ED(X_{n-1},Y_m) ED(Xn1,Ym) 以及 E D ( X n , Y m − 1 ) ED(X_{n},Y_{m-1}) ED(Xn,Ym1),而計算 E D ( X n − 1 , Y m ) ED(X_{n-1},Y_m) ED(Xn1,Ym) E D ( X n , Y m − 1 ) ED(X_{n},Y_{m-1}) ED(Xn,Ym1) 時都需要計算 E D ( X n − 1 , Y m − 1 ) ED(X_{n-1},Y_{m-1}) ED(Xn1,Ym1),因此具有重疊子問題。

遞迴地定義最優解的值:設 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[i1][j],dp[i][j1],dp[i1][j1]}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];
    }
};