1. 程式人生 > >字串匹配——Rabin–Karp algorithm

字串匹配——Rabin–Karp algorithm

        在https://mp.csdn.net/postedit/84679263這篇使用的是樸素的字串匹配方法,演算法複雜度為O(mn)(m為模式串長度,n為文字串長度)。在字串匹配任務中,Rabin–Karp演算法通過hash函式試圖加速文字子串與模式串的匹配過程的“逐一比對過程”

       這裡的hash函式可以將每個子串轉化為一個數值,因為存在這樣一個事實:如果兩個字串是相等的,則雜湊(函式)值相等,字串匹配問題也就被歸化為“計算模式串的雜湊值,然後在文字串中尋找相等雜湊值的子串”的問題。

       然而問題來了,一些不同的字串可能有相同的雜湊值,相同的雜湊值的兩字串可能不匹配,因此文字串中“潛在的匹配字串”與模式串還需要一一比對來再次確認,那麼對於越長的“潛在匹配子字串”需要花費更多的時間來再次確認。

虛擬碼如下:

1 	RabinKarpSearch(string s[1..n], string pattern[1..m])
2		hpattern := hash(pattern[1..m]);     		O(m)
3			for i from 1 to n-m+1
4				hs := hash(s[i..i+m-1])				O(m)
5				if hs = hpattern						O(n)
6					if s[i..i+m-1] = pattern[1..m]		O(m)
7						return i
8		return not found

       第五行要執行n-m+1次,每次比較都是O(1)。樸素的計算字串S[i+1…i+m]雜湊值的方法需要O(m)次,因為雜湊值計算在每次迴圈中都要進行,樸素雜湊計算方法需要O(mn)次,為了加速整個演算法,雜湊值計算必須變成常數時間內完成,一個小技巧就是變數hs已經包含先前字串s[i..i+m-1]的雜湊值。如果這個值被用來在常數時間內計算下一個字串的雜湊值,那麼連續計算雜湊值就非常的快,可以使用rolling hash來實現這一想法,一個簡單的但不是很好的rolling hash 函式僅僅加上子串中每個字元的值:hash(s[i+1…i+m])=hash(s[i…i+m-1])-s[i]+s[i+m],這個簡單的函式計算雜湊值使得產生雜湊碰撞的概率提高從而導致line5執行較多,好的效能需要良好的雜湊函式來減少line5的執行,因為逐個字元的比對需要O(m)次,整個演算法的最壞執行復雜度為O(mn)。所以這個hash函式的設計關乎整個演算法的效能

下面介紹一種雜湊函式,它將每個字串視作以某個很大的素數為基的數

比如基底是256,素數模是101,字串’hi’(104,105)的雜湊值計算為:

                                                  

       因為這個演算法的最壞複雜度O(mn),單個字串的匹配效能劣於KMP演算法和BM演算法,然而在多字串匹配應用上表現很好。為了在文字串中尋找k(一個較大的數字)個定長(長度都為m)模式串,RK演算法的一個變體使用一個布隆過濾器或者集合資料結構來檢查:給定字串的雜湊值是否屬於“模式串雜湊值集合”(要尋找的模式串們)。

虛擬碼如下:

                                      

       解釋一下,該演算法先將目標模式串們插入集合中,然後從文字串逐一進行計運算元串S[i:i+m-1]的雜湊值,如果雜湊值相等並且該串存在目標字串們的集合中則匹配成功一個目標字串。原理比較簡單,下一步就是來實現這個演算法。

參考文獻:

https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm

《The Introduction to Algorithms》