Rabin-Karp算法
Rabin-Karp算法是由M.O.Rabin和R.A.Karp发明的一种基于散列的字符串查找算法。
Rabin_karp算法的思想是通过将字符串的比较转回成数字的比较。比较两个长度为m的字符串是否相等需要O(m)时间,而比较两个数字是否相等通常可以是O(1)。为了将字符串映射称对应的数字,需要用到哈希函数。
这里存在一个问题就是利用哈希函数将字符串映射成对应的数字有可能发生哈希冲突,也就是说两个字符串通过哈希函数得到数字相等,但是这两个字符不同。Rabin-Karp的解决方法是,如果两个字符串通过哈希函数得到的数字相等,还需要将这两个字符利用朴素的方法进行比较。但是如果两个字符串哈希值不同,那么这两个字符串一定不同,这就降低比较的时间。
如果哈希值冲突很少,那么该算法的时间复杂度效果很好,最坏的情况就是每一个哈希值都冲突,那么这种情况算法的复杂度跟朴素算法相同,算上计算哈希值的开销,时间复杂度还要高于朴素算法。平均情况下,Rabin-Karp算法的时间复杂度为O(n+m).
Rabin-Karp 字符串编码的本质是对字符串进行哈希,将字符串之间的比较转化为编码之间的比较。在向右滑动窗口的过程中能够以O(1)的时间复杂度计算出下一位置的哈希值。另一个需要解决的问题是整数溢出的问题,Rabin-Karp是通过除余运算来解决的。
详情请看以下两个视频:
Rabin-Karp算法讲解视频
代码如下:
"""
Rabin Karp算法
t = (t-text[start-1]*aL + text[start+M-1])%modulus
"""
class RabinKarp:
def __init__(self):
# d是输入字符串中的字符数
self.d = 256
# 取余素数
self.modulus = 101
self.success = False
def search(self, pat, text):
print("pat:", pat)
print("text:", text)
M, N = len(pat), len(text)
h = 0
t = 0
for i in range(M):
h = (h * self.d + ord(pat[i])) % self.modulus
t = (t * self.d + ord(text[i])) % self.modulus
aL = 1
for i in range(M-1):
aL = (aL*self.d)%self.modulus
for start in range(N - M + 1):
if t == h:
# 编码相同还要按个字符进行比较
k = 0
for i in range(M):
if text[start + i] != pat[i]:
break
else:k+=1
if k == M:
print("匹配成功," + "pat在text中的位置是:", [start, start + M])
self.success = True
if start < N-M:
t = ((t - ord(text[start]) * aL)*self.d + ord(text[start + M])) % self.modulus
while t < 0:
t += self.modulus
if not self.success:
print("匹配失败:text中不存在pat")
KMP算法
class KMP:
def get_pnext(self,txt):
"""
生成针对txt中各位置i的下一检查位置表,用于KMP算法
"""
i, k , m = 0, -1, len(txt)
pnext = [-1]*m
while i<m-1:
if k==-1 or txt[i]==txt[k]:
i += 1
k += 1
if txt[i]==txt[k]:
pnext[i]=pnext[k]
else:
pnext[i]=k
else:
k = pnext[k]
return pnext
def matching_KMP(self, t, p, pnext):
j, i = 0, 0
n, m = len(t), len(p)
while j < n and i < m:
if i == -1 or t[j] == p[i]:
j, i = j+1, i+1
else:
i = pnext[i]
if i == m:
return j-i
return -1