1. 程式人生 > >最小編輯距離問題

最小編輯距離問題

最近在看一些react裡面的Virtual DOM的相關文章,看到了看到了 livoras 的這篇文章,其中講到了在比較兩棵虛擬DOM樹的差異的時候會用到字串最小編輯距離的演算法,因為那篇文章主要講述的點並不在此,所以對於這個演算法著墨不多,於是就認真去研究了下這個演算法,在此處做個記錄吧。

問題描述:

給定兩個字串m和n,只允許進行如下三種操作:

  1. 插入,例如:ab -> abc
  2. 刪除,例如:abc -> ab
  3. 替換,例如:abc -> abd

那麼請求出將m變成n的最小操作次數(也就是最小編輯距離)

求解這個問題,一般有兩種思路:遞迴和動態規劃。

遞迴:

首先假設字串m有j位,字串n有k位,此時我們將m -> n的最小編輯距離記為d[j][k],此時我們能總結出如下的規律:

  1. 當m[j] === n[k](m[j]和n[k]為字串的最後一位)時,例如:asd -> jkl時候,很明顯此時最後一位是不需要做變動的,因此最小編輯距離等同於:as -> jk,那麼我們可以確定d[j][k] === d[j - 1][k - 1];
  2. 當 m[j] !== n[k] 時,字串asd到字串jkl的d[j][k] 又可以分為如下三種情況:
    • asd -> jkl的最小編輯距離 = as -> jkl的最小編輯距離+1(加一個插入d的操作),對此可以描述為:d[j][k] === d[j - 1][k] + 1;
    • asd -> jkl的最小編輯距離 = asdl -> jkl的最小編輯距離 + 1(加一個刪除l的操作),此時可以看到asdl與jkl的最後一位相等,因此可以再次簡化為:asd -> jk的最小編輯距離 + 1,對此可以描述為:d[j][k] = d[j][k - 1] + 1;
    • asd -> jkl的最小編輯距離 = asl -> jkl的最下編輯距離 + 1(加上一個將d替換成l的操作),此時asl與jkl的最後一位再次相等,因此同樣可以進行簡化為:as -> jk的最下編輯距離 + 1,對此可以描述為:d[j][k] = d[j - 1][k - 1] + 1;
  3. 如果m的長度為 0,那麼 m -> n 的最小編輯距離為n的長度;反過來,如果n的長度為 0,那麼 m -> n 的最小編輯距離為m的長度(全部執行刪除操作),可以描述為:d[j][0] = j,d[0][k] = k;

那麼我們可以開始按照上述思路寫程式碼了:

/**
 * 遞迴演算法
 * @param {string} m
 * @param {string} n
 * @param {number} j 字串m的長度
 * @param {number} k 字串n的長度
 * @returns {number} 從 m -> n 的最小編輯距離
 */
function editDistance(m, n, j, k) {
    // 觸碰到邊界條件
    if (k === 0) {
        return j;
    } else if (j === 0) {
        return k;
    } else if (m[j - 1] === n[k - 1]) {
        // 當最後一位相等的時候
        return editDistance(m, n, j - 1, k - 1);
    } else {
        // 當最後一位不相等的時候,取最小值
        const d1 = editDistance(m, n, j - 1, k) + 1;
        const d2 = editDistance(m, n, j, k - 1) + 1;
        const d3 = editDistance(m, n, j - 1, k - 1) + 1;

        return Math.min(d1, d2, d3);
    }
}

這個程式碼雖然能實現,但是有個嚴重的問題,就是程式碼的效能很低下,時間複雜度是指數增長的,因此可以考慮另外一種實現。

動態規劃

動態規劃看起來跟遞迴很像,不過推理邏輯正好是反過來的。遞迴的邏輯是:“要求得 d[j][k],先要求得 d[j-1][k-1]……”,動態規劃的邏輯是:“先求得 d[j-1][k-1],再求 d[j][k]……”這是它們的主要區別。

同樣先舉個例子,有兩個字串分別是m = ‘asdfgh’ 和 n = ‘zscv’,我們一步步的來進行如下處理:

1:首先將我們的兩個字串放入如下的矩陣中去:

0 a s d f g h
0
z
s
c
v

2: 此時我們可以將d[0][0]、d[0][1] … d[0][4]等等的最小編輯距離填入此矩陣:

0 a s d f g h
0 0 1 2 3 4 5 6
z 1
s 2
c 3
v 4

3: 這個時候我們可以去計算d[1][1]了,上面在講述遞迴的方法的時候我們已經說過計算d[j][k]的時候,當m[j] !== n[k]時會有三種方法,此時 d[0][1] + 1 = 2、 d[1][0] + 1 = 2、d[0][0] + 1 = 1,因此可以得知d[1][1]的最小編輯距離就是1,然後這一行後面的我們都可以直接進行插入操作一次遞增即可,由此得出下面的矩陣:

0 a s d f g h
0 0 1 2 3 4 5 6
z 1 1 2 3 4 5 6
s 2
c 3
v 4

4:這個時候我們開始去計算d[2][2],首先我們可以按照之前的方式計算出d[1][2] = 2,填入矩陣,這個時候再看d[2][2],此時我們可以發現矩陣正好滿足條件m[j] === n[k],因此此時d[j][k] === d[j - 1][k - 1] = 1,填入矩陣如下:

0 a s d f g h
0 0 1 2 3 4 5 6
z 1 1 2 3 4 5 6
s 2 2 1 2 3 4 5
c 3
v 4

5:不斷重複上述步驟直到完成矩陣:

0 a s d f g h
0 0 1 2 3 4 5 6
z 1 1 2 3 4 5 6
s 2 2 1 2 3 4 5
c 3 3 3 2 3 4 5
v 4 4 4 4 3 4 5

此時我們自然可以看到d[j][k] = 5;

按照思路寫下程式碼:

/**
 * 動態規劃演算法
 * @param {string} m
 * @param {string} n
 * @return {number} 從 m → n 的最小編輯距離
 */
function dynamicPlanning(m, n) {
    const lenM = m.length;
    const lenN = n.length;
    const d = [];

    for (let i = 0; i <= lenM; i++) {
        d[i] = [];
        d[i][0] = i;
    }

    for (let j = 0; j <= lenN; j++) {
        d[0].push(j);
    }

    for (let i = 1; i <= lenM; i++) {
        for (let j = 1; j <= lenN; j++) {
            if (m[i - 1] === n[j - 1]) {
                d[i][j] = d[i - 1][j - 1];
            } else {
                const d1 = d[i - 1][j] + 1;
                const d2 = d[i][j - 1] + 1;
                const d3 = d[i - 1][j - 1] + 1;

                d[i][j] = Math.min(d1, d2, d3);
            }
        }
    }

    return d[lenM][lenN];
}

這次的演算法複雜度就為線性了

相關推薦

編輯距離

n-1 當前 color ++i 高效 i++ ring 代碼 urn 當前狀態一定不能從後面的狀態推出 解dp題步驟 1.定義dp數組 2.建立狀態轉移方程 3.確定初始狀態 4.驗證(循環順序) 題目描述 對於兩個字符串A和B,我們需要進行插入、刪除和修改操作將A串變

【動態規劃】編輯距離(字串A到字串B變化最少要多少步)

最小編輯距離是一道非常經典的動態規劃問題。 設A 和B 是2 個字串。要用最少的字元操作將字串A 轉換為字串B。  這裡所說的字元操作包括  (1)刪除一個字元;  (2)插入一個字元;  (3)將一個字元改為另一個字元。  將字串A變換為字串B 所用的最少字元操作次數也稱

編輯距離問題

最近在看一些react裡面的Virtual DOM的相關文章,看到了看到了 livoras 的這篇文章,其中講到了在比較兩棵虛擬DOM樹的差異的時候會用到字串最小編輯距離的演算法,因為那篇文章主要講述的點並不在此,所以對於這個演算法著墨不多,於是就認真去研究了下

動態規劃例項(八):編輯距離

    問題:給定一個長度為m和n的兩個字串,設有以下幾種操作:替換(R),插入(I)和刪除(D)且都是相同的操作。尋找到轉換一個字串插入到另一個需要修改的最小(操作)數量。     關於編輯距離    編輯距離(Edit Distance),又稱Levenshtein距離

計算編輯距離

#include <iostream> #include <vector> #include <string> #include <cmath> #include <algorithm> using namesp

編輯距離 | Minimum Edit Distance

關於兩個字串s1,s2的差別,可以通過計算他們的最小編輯距離來決定。 設A、B為兩個字串,狹義的編輯距離定義為把A轉換成B需要的最少刪除(刪除A中一個字元)、插入(在A中插入一個字元)和替換(把A中的某個字元替換成另一個字元)的次數,用ED(A,B)來表示。直

編輯距離及其C++實現

一、問題介紹: 本題提出了一些關於將字串x[1..m]轉換成y[1..n]的操作。 這些操作有複製、替代、刪除、插入、互換和終止。 這些操作所需的開銷是不同的,但每個操作的開銷都可以看是一個我們已經的常量,我們假設複製和替代這類操作的開銷要比插入和刪除這類操作的開銷少。 我

Levenshtein distance編輯距離演算法實現

Levenshtein distance,中文名為最小編輯距離,其目的是找出兩個字串之間需要改動多少個字元後變成一致。該演算法使用了動態規劃的演算法策略,該問題具備最優子結構,最小編輯距離包含子最小編輯距離,有下列的公式。 其中d[i-1,j]+1代表字串s2插入一個字母

編輯距離問題(Edition Distance)

注:這篇部落格討論的演算法是怎樣求解兩個字串的最小編輯距離,其目的是為了下一篇的虛擬DOM,來做一個預備工作,這裡主要討論的用LevenshteinDistance,主要通過的是動態規劃。 什麼是最小編輯距離: 給定一個長度為m和n的兩個字串,設有以下幾種操

編輯距離算法實現

編輯 length 一個 font then java實現 ron init system 一,算法介紹 在CS124課程的第一周提到 求解兩個字符串相似度的算法---Minimum Edit Distance(最短編輯距離)算法。該算法在NLP(自然語言處理)中也會用到。

編輯代價-golang

true 給定 src != 大小 n+1 lan 石頭 表示 題目: 給定兩個字符串str1和str2,在給定三個整數ic,dc和rc,分別代表插入、刪除和替換一個 字符,返回將str1編輯成str2的最小代價。 解題方法: 動態規劃。首先生成大小為(M+1)X(N+1)

算法56-----編輯代價【動態規劃】

狀態 tro 如果 for 字符串 技術 gin 給定 clas 一、題目:最小編輯代價 給定兩個字符串str1和str2,再給定三個整數ic,dc,rc,分別代表插入、刪除、替換一個字符的代價,返回將str1編輯成str2的最小代價。舉例:str1="abc" str

演算法56-----編輯代價【動態規劃】

一、題目:最小編輯代價 給定兩個字串str1和str2,再給定三個整數ic,dc,rc,分別代表插入、刪除、替換一個字元的代價,返回將str1編輯成str2的最小代價。舉例:str1="abc"   str2="adc"  ic=5    dc=3

編輯距離問題 : Levenshtein Distance

個人覺得只要你能明白edit陣列的含義就可以理解狀態轉移方程了。/* 可以用來表示字串的相似度? */ #include <bits/stdc++.h> using namespace s

!HDU 4311 曼哈頓距離-思維&卡時間-(橫縱座標分開算,排序)

題意:有n個點,求以這n個點中的某一點為起點,到各點的曼哈頓距離和最小是多少 分析: 暴力列舉又要超時,這種題一般都是考思維了,多半都是用技巧找到一個高效的方法。個人覺得這題跟上一篇文章的題是一個型別。這種思想要記住。 這題也是用“分治”,雖說題目要求的是曼哈頓距離,但是我

動態規劃C++實現--編輯代價

題目:給定兩個字串 str1 和 str2,再給定三個整數 ic, dc 和 rc,分別代表插入、刪除和替換1個字元的代價,          返回 str1 編輯成 str2 的代價。舉例:        str1 = "abc", str2 = "adc", ic = 5

編輯距離演算法(字串比較)

一、編輯距離 1、從字串a變為字串b所需要的元操作有3種: 增加一個字元刪除一個字元變化一個字元2、編輯距離:從字串a變為b所需要的最少操作步驟。 二、最短編輯距離(動態規劃) 首先定義一個函式——s

動態規劃---編輯距離

描述: 設A和B是2個字串。要用最少的字元操作將字串A轉換為字串B。這裡所說的字元操作包括: (1)刪除一個字元; (2)插入一個字元; (3)將一個字元改為另一個字元。 將字串A變換為字串B所用的

編輯距離(Edit Distance)【DP】

概念 編輯距離(最短編輯距離,Edit Distance)又稱Levenshtein Distance,“是指兩個字串之間,由一個轉成另一個所需的最少編輯操作次數。許可的編輯操作包括將一個

兩個字串之間的編輯距離

1.演算法原理編輯距離(Edit Distance)是指兩個字串之間,由一個轉成另一個所需的最少編輯操作次數,編輯操作包括增、刪、改操作。例如將kitten一字轉成sitting:sitten (k→s)sittin (e→i)sitting (→g),最短編輯距離為3.跟“