算法思想之動(dòng)態(tài)規(guī)劃(五)——最小編輯距離問(wèn)題

前言

今天我們繼續(xù)討論經(jīng)典的動(dòng)態(tài)規(guī)劃問(wèn)題之最小編輯距離問(wèn)題

最小編輯距離問(wèn)題

問(wèn)題描述

對(duì)于兩個(gè)字符串A和B,我們需要進(jìn)行插入、刪除和修改操作將A串變?yōu)锽串,定義c0,c1,c2分別為三種操作的代價(jià),請(qǐng)?jiān)O(shè)計(jì)一個(gè)高效算法,求出將A串變?yōu)锽串所需要的最少代價(jià)。例如將"abc"轉(zhuǎn)化為"adc",c0=5,c1=3,c2=100,最小代價(jià)為8。

問(wèn)題分析

我們先解釋下問(wèn)題描述中為什么最小代價(jià)是8。如果插入、刪除和修改操作的代價(jià)相同,顯然,"abc"->"adc"直接將'b'->'d'即可。但是由于多了c0=5,c1=3,c2=100的條件,所以直接進(jìn)行修改操作其代價(jià)為100,顯然不是最小代價(jià)。最小代價(jià)對(duì)應(yīng)的操作應(yīng)該是使用插入、刪除操作代替修改操作——先在'a'與'c'中插入'd',然后刪除'b',或者先刪除'b',在插入'd'。這樣最小代價(jià)為8。
其實(shí),該問(wèn)題實(shí)質(zhì)上是求解A \to B的最小編輯距離,只不過(guò)對(duì)每種操作賦予了權(quán)值。假設(shè)兩字符串A和B的長(zhǎng)度分別為nm。我們需要構(gòu)建一個(gè)(n+1) \times (m+1)的矩陣dp,代表A[0:i] \to B[0:j]的最小代價(jià)為dp[i][j]。可能你會(huì)疑問(wèn),為什么是(n+1) \times (m+1),而不是n \times m呢? 觀察下面的矩陣,你可能會(huì)找到答案。我們需要在兩字符串前添加空字符串來(lái)得到增加、刪除操作所對(duì)應(yīng)的代價(jià)作為初始值。

'' 'a' 'd' 'c'
'' 0 5 10 15
'a' 3
'b' 6
'c' 9

對(duì)于矩陣第0行第0列,代表由'' \to '',顯然代價(jià)為0,即dp[0][0] = 0;
對(duì)于矩陣第0行第1列,代表由'' \to a,其代價(jià)為c_0,即dp[0][1] = c_0 = 5;
對(duì)于矩陣第0行第2列,代表由'' \to ad,其代價(jià)為2*c_0,即dp[0][1] = 2 * c_0 = 10;
依次類推,dp[0][j] = j * c_0,0 \leq j \leq m
同樣的,對(duì)于dp[i][0] = i * c_1,0 \leq i \leq n
那么當(dāng) 1 \leq i \leq n, 1 \leq j \leq m時(shí),dp[i][j] = ?
下面,我們分兩種情況進(jìn)行討論:
(1) 當(dāng) A[i] == B[j]時(shí),可能的操作即最小代價(jià)有以下幾種情況:

  • 不需要進(jìn)行任何操作,此時(shí)最小代價(jià)就是A[0:i-1] \to B[0:j-1]的最小代價(jià),即dp[i-1][j-1]
  • A[0:i] \to B[0:j-1],然后增加B[j],此時(shí)最小代價(jià)為A[0:i] \to B[0:j-1]的最小代價(jià) + c_0,即dp[i][j-1] + c_0
  • 先將A[0:i-1] \to B[0:j],然后刪除A[i],此時(shí)最小代價(jià)為A[0:i-1] \to B[0:j]的最小代價(jià) + c_1,即dp[i-1][j] + c_1

此時(shí),dp[i][j] = min\{dp[i-1][j-1], dp[i][j-1] + c0, dp[i-1][j] + c1\};
(2) 當(dāng) A[i] \neq B[j]時(shí),可能的操作即最小代價(jià)有以下幾種情況:

  • 直接將A[i]替換為B[j],此時(shí)最小代價(jià)就是A[0:i-1] \to B[0:j-1]的最小代價(jià) + c_2,即dp[i-1][j-1] + c_2
  • A[0:i] \to B[0:j-1],然后增加B[j],此時(shí)最小代價(jià)為A[0:i] \to B[0:j-1]的最小代價(jià) + c_0,即dp[i][j-1] + c_0
  • 先將A[0:i-1] \to B[0:j],然后刪除A[i],此時(shí)最小代價(jià)為A[0:i-1] \to B[0:j]的最小代價(jià) + c_1,即dp[i-1][j] + c_1

此時(shí),dp[i][j] = min\{dp[i-1][j-1] + c_2, dp[i][j-1] + c0, dp[i-1][j] + c_1\};
需要注意的是,當(dāng)c_2 \geq c_0 + c_1時(shí),需要令c_2 = c_0 + c_1,這是因?yàn)樾薷牟僮骺梢杂迷黾?刪除操作代替,這樣的代價(jià)比直接進(jìn)行修改操作的代價(jià)要低。問(wèn)題分析一開(kāi)始也給出了說(shuō)明。

代碼實(shí)現(xiàn)

通過(guò)問(wèn)題分析,可以很容易得用代碼實(shí)現(xiàn),下面給出算法的java實(shí)現(xiàn)。

public class MinCost {
    public int findMinCost(String A, int n, String B, int m, int c0, int c1, int c2) {
        return core(A, n, B, m, c0, c1, c2);
    }

    public int core(String A, int n, String B, int m, int c0, int c1, int c2) {
        if (A.length() == 0 || B.length() == 0) {
            return 0;
        }
        A = " " + A;
        B = " " + B;
        int[][] dp = new int[n + 1][m + 1];
        // 初始化第0行
        dp[0][0] = 0;
        for (int i = 1; i < m + 1; i++) {
            dp[0][i] = c0 * i;
        }

        // 初始化第0列
        for (int j = 1; j < n + 1; j++) {
            dp[j][0] = c1 * j;
        }

        //update=delete+insert,如果update花費(fèi)更多就用delete+insert的花費(fèi)之和替換
        if (c2 >= c0 + c1) {
            c2 = c0 + c1;
        }

        for (int i = 1; i < n + 1; i++) {
            for (int j = 1; j < m + 1; j++) {
                if (A.charAt(i) == B.charAt(j)) {
                    //如果兩個(gè)字符串中A[i],B[j]的字符都一樣的
                    //1.什么都不做就行,0操作
                    int dontChange = dp[i - 1][j - 1];
                    //2.比如由abd→abcd=abc→ab+B串刪除c
                    int delete = dp[i - 1][j] + c1;
                    //3.比如由abcd→abcd=abcd→abc+B串插入d
                    int insert = dp[i][j - 1] + c0;
                    dp[i][j] = Math.min((Math.min(dontChange, delete)), insert);
                } else {
                    //1. A abcd → B abce = A abc→B abc + (A abcd → B abce, 替換d為e)
                    int replace = dp[i - 1][j - 1] + c2;
                    //2.比如由A abcd→B abce=A abc→B abce+B串刪除e
                    int delete = dp[i - 1][j] + c1;
                    //3.比如由A abcd→B abce=A abcd→B abc+B串插入d
                    int insert = dp[i][j - 1] + c0;
                    dp[i][j] = Math.min((Math.min(replace, delete)), insert);
                }
            }
        }
        return dp[n][m];
    }

    public static void main(String[] args) {
        MinCost minCost = new MinCost();
        String A = "abc";
        int n = A.length();
        String B = "adc";
        int m = B.length();
        int c0 = 3;
        int c1 = 5;
        int c2 = 3;
        int res = minCost.findMinCost(A, n, B, m, c0, c1, c2);
        System.out.println(res);
    }
}

經(jīng)典問(wèn)題

未來(lái)幾篇博文,我將繼續(xù)對(duì)經(jīng)典的動(dòng)態(tài)規(guī)劃問(wèn)題進(jìn)行整理,敬請(qǐng)關(guān)注~
由于本人水平有限,文章難免有欠妥之處,歡迎大家多多批評(píng)指正!

寫在最后

歡迎大家關(guān)注我的個(gè)人博客復(fù)旦猿

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容