您的位置:首页 > 其它

哈希表(三)

2016-07-10 08:19 162 查看
字符串搜索(搜索模板)

给定一个文本T(书,网站,简介)和一个模板P(字,短语,句子),找出P在T中出现的次数

例子

在网站中寻找自己的名字

在推特消息中寻找自己的公司

检测被病毒感染的文件——代码模板

字串标示

使用S[i…j]标示字符串S从位置i到j的字串

输入:字符串T和P

输出:所有P在T中出现的位置i,其中0 <= i <= |T| - |P|,T[i..i+|P|-1] = P

一般实现算法

从0到|T| - |P|的每个位置,一个字节接一个字节的检查T[i..i+|P|-1]是否相等,如果相等就把i添加到result里

伪代码实现

AreEqual(s1, s2)

if |s1| != |s2|:
return False
for i from 0 to |s1|-1:
if s1[i] != s2[i]:
return False
return True


FindPateernNaive(T, P)

result <--- empty list
for i from 0 to |T| - |P|:
if AreEqual(T[i..i+|P|-1], P)
result.append(i)
return result


时间复杂度

FindPateernNaive(T, P)的时间复杂度为O(|P||T|)

Rabin Karp 算法

P需要和字符串中长度为|P|的所有子串进行比较

使用哈希值实现P和T子串的快速比较

思想

if h(P) != h(S),那么确定P != S

if h(P) = h(S),调用AreEqual(P, S)

使用素数为p的多项式哈希函数集Pp

如果P=S,那么多项式哈希函数h(P)=h(S)的最大概率为Pr[h(P)=h(S)]=|P|/p

伪代码实现

RabinKarp(T, P)

p <--- big prime, x <--- random(1, p-1)
result <---- empty list
pHash <---- polyHash(P, p, x)
for i from 0 to |T|-|P|:
tHash <---- polyHash(T[i..i + |P| - 1])
if pHash != tHash
continue
if AreEqual(T[i..i + |P| - 1], P):
result.append(i)
return result


错误警告

错误警告是指当P和T[i..i + |P| -1]比较时,pHash = tHash,但是 P != T[i..i + |P| -1]

易知,发生假警报的最大概率为:|P|/p,所以发生假警报的平均数目为:(|T| - |P| + 1)*|P|/p,当p的值取得很大时,发生假警报的平均数目会很小

时间复杂度分析

AreEqual的时间复杂度为O(|P|)

只有当h(P) = h(T[i..i + |P| -1])时,AreEqual才被调用,也就是在发生假警报时被调用

通过选择p >> |T||P|,可以使得发生假警报的次数微乎其微

如果p在T中出现的次数为q,那么AreEqual总的时间复杂度为O((q +(|T| + 1 - |P|) * |P|) * |P| = O(q|P|) 当 q << |P||T|

总的时间复杂度为O(|T||P|) + O(q|P|)

改进时间复杂度

多项式哈希函数的计算方法如下



字符串T的两个连续子串的多项式哈希值是非常相似的.若把h[i..i+|P|-1]记为H[i],则有:



经过变换可得到下式:



根据此式,可以利用新算法实现Rabin Karp算法(时间复杂度为O(|P| + |T|))

伪代码实现

PrecomputeHash[T, |P|, p, x]

H <---- array length of |T| - |P| + 1
S <---- T[|T|-|P|..|T| - 1]
H[|T| - |P|] <--- polyHash(S, p, x)
y <---- 1
for i from1 to |P|:
y <---- (y * x) mod p
for i from |T| - |P| - 1 down to 0:
H[i] <---- (x*H[i + 1] + T[i] - y * T[i + |P|])
return H


RabinKarp(T, P)

P <---- big prime, x <---- random(1,p-1)
result < empty list
pHash <---- polyHash(P, p, x)
H <---- PrecomputeHashes(T, |P|, p, x)

for i from 0 to |T| - |P|:
if pHash != H[i]:
continue
if AreEqual(P, T[i..i+|P|-1])
result.append(i)
return result


时间复杂度分析

h(P)的时间复杂度为O(|P|)

PrecomputeHashes的时间复杂度为O(|P| + |T|)

AreEqual的平均总时间复杂度为O(q|P|)其中q是P在T中出现的次数

平均时间复杂度:O(|T|+(q+1)|P|)

通常情况下,q是很小的,所以时间复杂度远远小于O(|P||Q|)

总结

哈希表存储集合和映射时非常有用的

搜索和修改的平均时间复杂度可以达到O(1)

一定要使用好的哈希函数集和随机性

哈希值在处理文本字符串的时候也是非常有用的
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: