背景
Rabin-Karp字符串匹配算法和前面介紹的《樸素字符串匹配算法》類似,也是對應每一個字符進行比較,不同的是Rabin-Karp採用了把字符進行預處理,也就是對每個字符進行對應進制數並取模運算,類似於通過某種函數計算其函數值,比較的是每個字符的函數值。預處理時間O(m),匹配時間是O((n-m+1)m)。
Rabin-Karp算法的思想:
- 假設待匹配字符串的長度爲M,目標字符串的長度爲N(N>M);
- 首先計算待匹配字符串的hash值,計算目標字符串前M個字符的hash值;
- 比較前面計算的兩個hash值,比較次數N-M+1:
- 若hash值不相等,則繼續計算目標字符串的下一個長度爲M的字符子串的hash值
- 若hash值相同,則需要使用樸素算法再次判斷是否爲相同的字串;
僞代碼
Rabin_Karp_search(T, P, d, q)
n = T.length;
m = P.length;
h = d^(m-1)mod q;
p = 0;
t = 0;
for i =1 to m
p = (d*p+P[i]) mod q;
t = (d*t+T[i])mod q;
for i = 0 to n-m
if p==t
if P[1..m]==T[i+1..i+m]
print"Pattern occurs with shift"i
if i<n-m
t = d(t-T[i+1]h) + T[i+m+1]mod q
其中
- d表示字母表的字母個數,ascii值爲0~127的字符。如果採用小寫英文字母來做字母表的話,那麼d就是26。
h=dm−1%q 。其實模q運算應該是可有可無的,加入q應該是爲了避免數據溢出。但是加入模q後,由ts == p mod q不能說明ts == p,不過ts != p mod q則可以肯定ts != p。所以q最好選擇比較大的質數,並且選取的q要滿足使d,q的值在一個計算機字長內。如果使用的是動態的編程語言的話就不用擔心數據溢出,因此就不用進行模q運算。
代碼實現
# -*- coding: utf-8 -*-
"""
Created on Thu Jul 28 23:11:27 2016
@author: zang
"""
def Rabin_Karp(text, pattern, d, q):
n = len(text)
m = len(pattern)
h = pow(d,m-1)%q
p = 0
t = 0
flag = 0
results = []
for i in range(m): # preprocessing
p = (d*p+ord(pattern[i]))%q
t = (d*t+ord(text[i]))%q
for s in range(n-m+1): # note the +1
if p == t: # check character by character
match = True
for i in range(m):
if pattern[i] != text[s+i]:
match = False
break
if match:
flag += 1
results.append(" "*s + pattern + " "*(n - m - s) + " " + str(s+1) + " " + str(flag))
if s < n-m:
t = (t-h*ord(text[s]))%q # remove letter s
t = (t*d+ord(text[s+m]))%q # add letter s+m
t = (t+q)%q # make sure that t >= 0
if flag == 0:
print "No find."
else:
print flag," matching results are listed below."
print "-------" + "-"*t + "-------"
print text
for line in results:
print line
print "-------" + "-"*t + "-------"
def main():
while 1:
text = raw_input("text: ")
pattern = raw_input("pattern: ")
d = int(raw_input("charsize: "))
q = int(raw_input("mod number: "))
if len(text) == 0 or len(pattern) == 0:
print "\nplease input text and pattern again!"
break
Rabin_Karp(text, pattern,d,q)
if __name__ == '__main__':
main()