背景
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()