搜尋引擎裡有乙個很重要的話題,就是文字糾錯,主要有兩種做法,一是從詞典糾錯,一是分析使用者搜尋日誌,今天我們**使用基於詞典的方式糾錯,核心思想就是基於編輯距離,使用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...