從編輯距離 BK樹到文字糾錯

2021-09-07 15:51:38 字數 2862 閱讀 5045

搜尋引擎裡有乙個很重要的話題,就是文字糾錯,主要有兩種做法,一是從詞典糾錯,一是分析使用者搜尋日誌,今天我們**使用基於詞典的方式糾錯,核心思想就是基於編輯距離,使用bk樹。下面我們來逐一**:

2023年,**科學家vladimir

levenshtein給字串相似度做出了乙個明確的定義叫做levenshtein距離,我們通常叫它「編輯距離」。

字串a到b的編輯距離是指,只用插入、刪除和替換三種操作,最少需要多少步可以把a變成b。例如,從fame到gate需要兩步(兩次替換),從game到acm則需要三步(刪除g和e再新增c)。levenshtein給出了編輯距離的一般求法,就是大家都非常熟悉的經典動態規劃問題。

class levenshteindistancefunction 

public int distance(charsequence left, charsequence right)

// swap rows, use last row for next row.

int t = currentrow;

currentrow = nextrow;

nextrow = t;

}return currentrow[rightlength];}}

編輯距離的經典應用就是用於拼寫檢錯,如果使用者輸入的詞語不在詞典中,自動從詞典中找出編輯距離小於某個數n的單詞,讓使用者選擇正確的那乙個,n通常取到2或者3。

這個問題的難點在於,怎樣才能快速在字典裡找出最相近的單詞?可以像 使用貝葉斯做英文拼寫檢查(c#) 裡是那樣,通過單詞自動修改乙個單詞,檢查是否在詞典裡,這樣有暴力破解的嫌疑,是否有更優雅的方案呢?

2023年,burkhard和keller提出的bk樹有效地解決了這個問題。bk樹的核心思想是:

令d(x,y)表示字串x到y的levenshtein距離,那麼顯然:

d(x,y) = 0 當且僅當 x=y (levenshtein距離為0 <==> 字串相等)

d(x,y) = d(y,x) (從x變到y的最少步數就是從y變到x的最少步數)

d(x,y) + d(y,z) >= d(x,z) (從x變到z所需的步數不會超過x先變成y再變成z的步數)

最後這乙個性質叫做三角形不等式。就好像乙個三角形一樣,兩邊之和必然大於第三邊。

首先我們隨便找乙個單詞作為根(比如game)。以後插入乙個單詞時首先計算單詞與根的levenshtein距離:如果這個距離值是該節點處頭一次出現,建立乙個新的兒子節點;否則沿著對應的邊遞迴下去。例如,我們插入單詞fame,它與game的距離為1,於是新建乙個兒子,連一條標號為1的邊;下一次插入gain,算得它與game的距離為2,於是放在編號為2的邊下。再下次我們插入gate,它與game距離為1,於是沿著那條編號為1的邊下去,遞迴地插入到fame所在子樹;gate與fame的距離為2,於是把gate放在fame節點下,邊的編號為2。

如果我們需要返回與錯誤單詞距離不超過n的單詞,這個錯誤單詞與樹根所對應的單詞距離為d,那麼接下來我們只需要遞迴地考慮編號在d-n到d+n範圍內的邊所連線的子樹。由於n通常很小,因此每次與某個節點進行比較時都可以排除很多子樹

可以通過下圖(來自 超酷演算法(1):bk樹 (及個人理解))理解:

知道了原理實現就簡單了,這裡從github找一段**

建樹:

public boolean add(t t) 

nodeparentnode = rootnode;

integer distance;

while ((distance = distancefunction.distance(parentnode.item, t)) != 0

|| !t.equals(parentnode.item))

parentnode = childnode;

}return false;

}

查詢:

public list> search(t t, int radius) 

}searchresults.trimtosize();

collections.sort(searchresults);

return collections.unmodifiablelist(searchresults);

}

準備詞典,18萬的影視名稱:

測試**:

static void outputsearchresult( list> results)

}static void test(bktreetree,string word)

public static void main(string args)

system.out.println("建樹耗時:"+(system.currenttimemillis()-starttime)+"ms");

starttime = system.currenttimemillis();

string testwords = new string;

for (string testword: testwords)

system.out.println("測試耗時:"+(system.currenttimemillis()-starttime)+"ms");

}

結果:

詞典條數:18513

建樹耗時:421ms

湄公河兇案的最相近結果:

湄公河大案

葫蘆絲兄弟的最相近結果:

葫蘆兄弟

少林足球的最相近結果:

少林足球

笑林足球

測試耗時:20ms

參考:

bk樹 編輯距離演算法

演算法出處 matrix67大神,除了字串匹配 查詢回文串 查詢重複子串等經典問題以外,日常生活中我們還會遇到其它一些怪異的字串問題。比如,有時我們需要知道給定的兩個字串 有多像 換句話說兩個字串的相似度是多少。1965年,科學家vladimir levenshtein給字串相似度做出了乙個明確的定...

從B樹到B 樹

最近在看作業系統和資料庫系統,當涉及到查詢檔案和建立資料庫索引時書中反覆提到使用b 樹可以實現高效的查詢,於是我迫不及待地想研究一下b 樹的內部結構。首先從二叉查詢樹開始講起。二叉搜尋樹 binarysearch tree 別名又叫二叉查詢樹,二叉排序樹。它是一棵空樹或者是滿足以下條件的二叉樹 它的...

MFC從編輯框取出資料儲存檔案到TXT檔案中

if filedlg.domodal idok 彈出對話方塊 cstdiofile file cstring a cstring strtext t file.open greadfilepathname1,cfile modecreate cfile modewrite cfile typetex...