Levenshtein distance 编辑距离算法
這幾天再看 virtrual-dom,關(guān)于兩個(gè)列表的對(duì)比,講到了 Levenshtein distance 距離,周末抽空做一下總結(jié)。
Levenshtein Distance 介紹
在信息理論和計(jì)算機(jī)科學(xué)中,Levenshtein 距離是用于測(cè)量?jī)蓚€(gè)序列之間的差異量(即編輯距離)的度量。兩個(gè)字符串之間的 Levenshtein 距離定義為將一個(gè)字符串轉(zhuǎn)換為另一個(gè)字符串所需的最小編輯數(shù),允許的編輯操作是單個(gè)字符的插入,刪除或替換。
例子
‘kitten’和’sitten’之間的 Levenshtein 距離是 3,因?yàn)橐幌氯齻€(gè)編輯將一個(gè)更改為另一個(gè),并且沒(méi)有辦法用少于三個(gè)編輯來(lái)執(zhí)行操作。
k?itten?sitten => 用’s’代替’k’- sitt?
e?n sitt?i?=> 用’i’代替’e’ - sittin sittin?
g?在結(jié)尾插入’g’
Levenshtein Distance (編輯距離) 算法詳解
為了得到編輯距離,我們用 beauty 和 batyu 為例:
圖示如?①?單元位置是兩個(gè)單詞的第一個(gè)字符 [b] 比較得到的值,其的值有它的上方的值 (1)、它左方的值 (1) 和它左上角的值 (0) 來(lái)決定。當(dāng)單元格所在的行和列所對(duì)應(yīng)的字符相等時(shí),單元格的值為左上方的值。
否則,單元格左上角的值與其上方和左方的值進(jìn)行比較,它們之間的最小值 + 1 即是單元格的值。
圖中?①?的值由于單元格行和列相等,所以取左上角值 0。
圖中?②?的值由于單元格行列不相等,(1, 2, 0) 取最小為 0, 結(jié)果 + 1, 所以?②?值為 1。
圖示?③?的值由于單元格行列不相等,(1, 0, 2) 取最小 0, 結(jié)果 + 1, 所以?③?值為 1。
算法證明
這個(gè)算法計(jì)算的是將 s [1…i] 轉(zhuǎn)換為 t [1…j](例如將 beauty 轉(zhuǎn)換為 batyu)所需最少的操作數(shù)(也就是所謂的編輯距離),這個(gè)操作數(shù)被保存在 d [i,j](d 代表的就是上圖所示的二維數(shù)組)中。
- 在第一行與第一列肯定是正確的,這也很好理解,例如我們將 beauty 轉(zhuǎn)換為空字符串,我們需要進(jìn)行的操作數(shù)為 beauty 的長(zhǎng)度(所進(jìn)行的操作為將 beauty 所有的字符丟棄)。
- 我們對(duì)字符的可能操作有三種:
- 將 s [1…n] 轉(zhuǎn)換為 t [1…m] 當(dāng)然需要將所有的 s 轉(zhuǎn)換為所有的 t,所以,d [n,m](表格的右下角)就是我們所需的結(jié)果。
- 如果我們可以使用 k 個(gè)操作數(shù)把 s [1…i] 轉(zhuǎn)換為 t [1…j-1],我們只需要把 t [j] 加在最后面就能將 s [1…i] 轉(zhuǎn)換為 t [1…j],操作數(shù)為 k+1
- 如果我們可以使用 k 個(gè)操作數(shù)把 s [1…i-1] 轉(zhuǎn)換為 t [1…j],我們只需要把 s [i] 從最后刪除就可以完成轉(zhuǎn)換,操作數(shù)為 k+1
- 如果我們可以使用 k 個(gè)操作數(shù)把 s [1…i-1] 轉(zhuǎn)換為 t [1…j-1],我們只需要在需要的情況下(s [i] != t [j])把 s [i] 替換為 t [j],所需的操作數(shù)為 k+cost(cost 代表是否需要轉(zhuǎn)換,如果 s [i]==t [j],則 cost 為 0,否則為 1)。
可能的改進(jìn)
- 現(xiàn)在的算法復(fù)雜度為 O (m*n),可以將其改進(jìn)為 O (m)。因?yàn)檫@個(gè)算法只需要上一行和當(dāng)前行被存儲(chǔ)下來(lái)就可以了。
- 如果需要重現(xiàn)轉(zhuǎn)換步驟,我們可以把每一步的位置和所進(jìn)行的操作保存下來(lái),進(jìn)行重現(xiàn)。
- 如果我們只需要比較轉(zhuǎn)換步驟是否小于一個(gè)特定常數(shù) k,那么只計(jì)算高寬寬為 2k+1 的矩形就可以了,這樣的話,算法復(fù)雜度可簡(jiǎn)化為 O (kl),l 代表參加對(duì)比的最短 string 的長(zhǎng)度。
- 我們可以對(duì)三種操作(添加,刪除,替換)給予不同的權(quán)值(當(dāng)前算法均假設(shè)為 1,我們可以設(shè)添加為 1,刪除為 0,替換為 2 之類的),來(lái)細(xì)化我們的對(duì)比。
- 如果我們將第一行的所有 cell 初始化為 0,則此算法可以用作模糊字符查詢。我們可以得到最匹配此字符串的字符串的最后一個(gè)字符的位置(index number),如果我們需要此字符串的起始位置,我們則需要存儲(chǔ)各個(gè)操作的步驟,然后通過(guò)算法計(jì)算出字符串的起始位置。
- 這個(gè)算法不支持并行計(jì)算,在處理超大字符串的時(shí)候會(huì)無(wú)法利用到并行計(jì)算的好處。但我們也可以并行的計(jì)算 cost values(兩個(gè)相同位置的字符是否相等),然后通過(guò)此算法來(lái)進(jìn)行整體計(jì)算。
- 如果只檢查對(duì)角線而不是檢查整行,并且使用延遲驗(yàn)證(lazy evaluation),此算法的時(shí)間復(fù)雜度可優(yōu)化為 O (m (1+d))(d 代表結(jié)果)。這在兩個(gè)字符串非常相似的情況下可以使對(duì)比速度速度大為增加。
字符串比較代碼
這一部分的代碼,參考了?https://rosettacode.org/wiki/Levenshtein_distance#ES5
| |
還原字符串
上面總結(jié)了傳統(tǒng)的計(jì)算字符串之間的差距,那么當(dāng)我們?cè)趺茨茉谟?jì)算的過(guò)程中,記錄需要轉(zhuǎn)換的步驟,并且進(jìn)行還原呢。
這里我們需要對(duì)比較的每一位的步驟有一個(gè)了解。
為了得到編輯距離,我們用 beauty 和 batyu 為例:
從上面一節(jié)的圖中可以看到,'beauty'?轉(zhuǎn)換為?''?,對(duì)一個(gè)的第一行的?[1,2,3,4,5,6],每一個(gè)步驟都相對(duì)與上一個(gè)元素新建一個(gè)元素,同理?''?轉(zhuǎn)換為?'batyu',每一個(gè)值都是相對(duì)一上一個(gè)元素的刪除步驟。
那么對(duì)角線也顯而易見(jiàn)就是先相對(duì)于替換操作。那么我們現(xiàn)在需要做的就是,記錄下相對(duì)應(yīng)的索引和元素以及需要進(jìn)行的操作,并將其保存為一個(gè)對(duì)象,每次新增的對(duì)象用數(shù)組來(lái)保存就可以了。
| |
下面是 compare 方法:
| |
上面需要注意的是,我們一組保存了多個(gè)數(shù)組對(duì)象,不要對(duì)原數(shù)組進(jìn)行操作,每一次操作我們都需要拷貝一個(gè)新的數(shù)組對(duì)象。
具體的的 diff 代碼參考?diff 代碼
得到了,patches 對(duì)象,剩下的我們就需要 patch 了
| |
具體代碼參考代碼地址
參考資料
- http://www.cnblogs.com/zhoug2020/p/4224866.html
- https://rosettacode.org/wiki/Levenshtein_distance#ES5
總結(jié)
以上是生活随笔為你收集整理的Levenshtein distance 编辑距离算法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: python中break和continu
- 下一篇: 字符串编辑距离(Edit Distanc