哈希表(三)
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)
FindPateernNaive(T, P)
时间复杂度
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和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]
RabinKarp(T, P)
时间复杂度分析
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)
一定要使用好的哈希函数集和随机性
哈希值在处理文本字符串的时候也是非常有用的
给定一个文本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)
一定要使用好的哈希函数集和随机性
哈希值在处理文本字符串的时候也是非常有用的
相关文章推荐
- 用js怎么取table中的tr标签--这是一个陷阱
- windows10安装Oracle提示错误-INS-13001环境不满足最低要求
- Android布局优化之ViewStub、include、merge使用与源码分析
- Java中Map根据键值(key)或者值(value)进行排序实现
- Uva11396 Claw Decomposition(爪分解)
- HDU2051 Bitset
- Android 如何直播RTMP流
- 15、JS回调函数与call函数
- java如何对map进行排序详解(map集合的使用)
- StrPCopy与StrPas功能正好相反,作用是与C语言字符串和Delphi的String相互转化
- C# mouse keyboard monitor
- 【算法-2】判断字符串是否为回纹串
- linq按条件sum
- Comparable的使用
- Docker中部署Kubernetes
- Contiki开发3:调试平台与Debug系统
- JavaScript生态圈现状:初学者地图
- Linux运维经验分享与思路
- 生命与负熵---宇宙的心弦
- (05)String类型的集合映射