第三十二章 字符串匹配
来源:互联网 发布:hits算法计算三轮 编辑:程序博客网 时间:2024/06/02 09:01
设T为全局查询域,P为要查询的字符串,查询结果是:返回一个或多个距离T开始的偏移
后缀:字符串A是字符串D=X+A的后缀,记A -} D
前缀:字符串A是字符串D=A+x的前缀,机A {- D
算法:
代码实现:
t(s+1)=(d*(t(s)-T[s+1]*h)+T[s+m+1])mod q t(s)=T[s+1,s+2,....s+m](mod q)
算法:
代码实现:
我们也可以将P的m的字符求和然后模q,p=(P[1]+P[2]+...P[m])mod q
然后求出t0=(T[1]+T[2]+...T[m])mod q,也可以在常数时间内求出t(s+1)
根据公式:t(s+1)=(t(s)-T[s]+T[s+m+1])mod q
状态0:T[s]!=P[1]
状态1:P[1]
状态2:P[1]->P[2]
状态3:P[1]->P[2]->P[3]
.
.
.
状态m:P[1]->P[2]->P[3]...->P[m]
如果T中存在字符串P则T中一定存在一个这样的状态P[1]->P[2]...->P[m],只有m状态才是正确匹配到了P
当在状态i的时候,设T[k]=P[i]如果T[k+1]!=P[i+1],则状态不会转移到i+1。那么状态会转移到什么呢?答案是任何<i的状态都有可能,这是需要经过计算的。
存在字符串A是P[1,2,...i]的后缀&&是P的前缀,我们转移到最长A的状态中,因为这是紧接下来的第一个可能成功匹配的偏移。(这一点将会在后面证明)
设集合G={T中出现过的字符},我们把状态 i 经过字符c(each c from G)转移到状态 j 计算出来并保存到一个二维数组theta中即j=theta(i,c)
例如:G={a,b,c} P=ababaca 结果如图:
计算theta的一种算法如下
算法:
定义:pai[i]的值为最长P[1...i]的真后缀同时是P[1....m]的前缀的长度
我们先假设:"每当T[s+1...s+i]的下一个字符不能匹配时,将偏移s向前移i-pai[i]位(s=s+i-pai[i])继续向前匹配,这样能匹配到所有的结果"是正确的
现在来说明假设的正确性
T[s+1...s+i]能正确匹配,T[s+i+1]不能。此时我们将s直接向前移动i-pai[i]位,那么接下来的pai[i]位与P[1...pai[i]]是匹配的,可以继续向前匹配。只要说明之间跳过的点不能匹配到正确结果就可证明假设成立。
如何计算pai
kmp算法:
后缀:字符串A是字符串D=X+A的后缀,记A -} D
前缀:字符串A是字符串D=A+x的前缀,机A {- D
朴素算法
这个算法就是每个人都能想到的最朴素的算法算法:
naive_string_matcher(T,P) n=T.lenth m=P.lenth for s=0 to n-m if P[1...m]==T[s+1...s+m] printf s
代码实现:
Rabin-Karp
我们主要是体会一下这个算法思想,因为它假设字符出自集合{0,1,2...9}(10进制,所以设变量d=10)这样就可以将要查询的P看成一个m=P.length位的数,然后对一个数q求模。如果两个m位字符串代表的数模q相等,则这两个字符串才有可能相等,进一步判断如果相等输出偏移量。1、先求出h=dm-1(mod q)
2、求出P和T的前m个字符串代表数的模p=P[1,2,...m](mod q)、 t0=T[1,2,...m](mod q)的值
3、已知t(i)的值,我们能在常数时间求出t(i+1)的值,根据公式t(s+1)=(d*(t(s)-T[s+1]*h)+T[s+m+1])mod q t(s)=T[s+1,s+2,....s+m](mod q)
算法:
ranin_karp_matcher(T,P,d,q) n=T.length m=P.length h=d(m-1)mod q p for i=1 to m //除法求余 p=(d*p+P[i])mod q t0=(d*t0+T[i])mod q for s=0 to n-m if p==t(s) if P[1...m]==T[s+1...s+m] printf s if s<n-m t(s+1)=(d*(t(s)-T[s+1]*h)+T[s+m+1])mod q
代码实现:
我们也可以将P的m的字符求和然后模q,p=(P[1]+P[2]+...P[m])mod q
然后求出t0=(T[1]+T[2]+...T[m])mod q,也可以在常数时间内求出t(s+1)
根据公式:t(s+1)=(t(s)-T[s]+T[s+m+1])mod q
有限自动机
状态0:T[s]!=P[1]
状态1:P[1]
状态2:P[1]->P[2]
状态3:P[1]->P[2]->P[3]
.
.
.
状态m:P[1]->P[2]->P[3]...->P[m]
如果T中存在字符串P则T中一定存在一个这样的状态P[1]->P[2]...->P[m],只有m状态才是正确匹配到了P
当在状态i的时候,设T[k]=P[i]如果T[k+1]!=P[i+1],则状态不会转移到i+1。那么状态会转移到什么呢?答案是任何<i的状态都有可能,这是需要经过计算的。
存在字符串A是P[1,2,...i]的后缀&&是P的前缀,我们转移到最长A的状态中,因为这是紧接下来的第一个可能成功匹配的偏移。(这一点将会在后面证明)
设集合G={T中出现过的字符},我们把状态 i 经过字符c(each c from G)转移到状态 j 计算出来并保存到一个二维数组theta中即j=theta(i,c)
例如:G={a,b,c} P=ababaca 结果如图:
计算theta的一种算法如下
算法:
compute_transition_function(P,G) m=P.length for q=0 to m for(each charater a from G) k=min(m+1,q+2)//仔细斟酌 k= 1 to m+1 do k=k-1 //P[1..k]是P[1..q]+a的后缀,由k=k-1可知这里去的是最长的A while P[1,2,...k]-}P[1,2,...q]+a //k=0时空字符串是任何字符串的后缀 theta(q,a)=k
计算出theta后就可以在线性时间完成匹配
算法:
finite_automaton_matcher(T,theta,m) n=T.length q=0 for i=1 to n//注意是[0,n] q=theta(q,T[i]) if q==m print i-m
代码实现:
Knuth-Morris-Pratt
定义:pai[i]的值为最长P[1...i]的真后缀同时是P[1....m]的前缀的长度
我们先假设:"每当T[s+1...s+i]的下一个字符不能匹配时,将偏移s向前移i-pai[i]位(s=s+i-pai[i])继续向前匹配,这样能匹配到所有的结果"是正确的
现在来说明假设的正确性
T[s+1...s+i]能正确匹配,T[s+i+1]不能。此时我们将s直接向前移动i-pai[i]位,那么接下来的pai[i]位与P[1...pai[i]]是匹配的,可以继续向前匹配。只要说明之间跳过的点不能匹配到正确结果就可证明假设成立。
如果s到s+i-pai[i]之间有能够正确匹配的,那么存在一个s'使得T[s'+1...s+i]是P[1...m]的前缀,且值大于pai[i]。这与pai[i]最长矛盾。
求这样一个pai的原因是:如果T[s+1...s+i]与P[1...i]匹配,下一个不能匹配即T[s+i+1]!=P[i+1]。站在偏移s处可以看到T[s+1...s+i]的值,因为我们知道P[1...i]的值,他们两者相等!所以应该预先求出每个P[1...i]的pai[i],以免每次求最长T[s+1....s+i]的真后缀同时是P[1...m]的前缀。根据pai[i]的值,于是我们设置偏移s=s+i-pai[i]。
如何计算pai
现在pai[i]的值为k,如果能匹配就pai[i+1]=k+1。如果不能匹配就递归的考虑与P[i+1]能否匹配,能匹配时pai[i+1]=k+1,不能匹配时k=pai[k]。直到k=0时结束,并令pai[i+1]=0。
当pai[i]=k=0时,就回到了最初的匹配,P[i+1]是否等于P[k+1]。
算法:
compute_prefix_function(P)m=P.lengthlet pai[1...m] be a new arraypai[1]=0k=0for q = 2 to m//find q <--> kwhile k>0 and P[k+1]!=P[q]k=pai[k]if P[k+1]==P[q]k=k+1pai[q]=kreturn pai
kmp算法:
KMP_matcher(T,P)n=T.lengthm=P.lengthpai=COMPUTE_PREFIX_FUNCTION(P)q=0//number of characters matchedfor i = 1 to n //scan the text from left to rightwhile q>0 and T[i+1]!=P[q]q=pai[q]//next character does not matchif T[i+1]==P[q]q=q+1if q==m //is all of P matchedprint "Patter occurs with shift" i-mq=pai[q]//look for the next match
代码实现:
未完待续...
0 0
- 第三十二章 字符串匹配
- 算法导论第三十二章-字符串匹配-Cpp代码实现
- 算法导论 第三十三章:字符串匹配
- 第三章 第三十二题
- 第三十二章 连词
- 《道德经》第三十二章
- [算法系列之十二]字符串匹配之蛮力匹配
- 第三章第四十二题
- 第三章 第四十二题
- 大话数据结构十二:字符串的模式匹配(BM算法)
- 171124-字符数组与字符串【连续第三十二天】
- 第三十二章 反射的更多细节
- 第三十二章 SpringBoot使用application配置文件
- 字符串匹配算法 【微软面试100题 第三十三题】
- hiho第三周——字符串匹配KMP算法
- 第三章 字符串类型
- 《Python 第三章》字符串
- 第三章字符串
- 自定义滑动开关控件的实现与使用
- 详解垃圾回收机制
- Android 四大图片缓存原理,特性对比
- 处理方法中的数据绑定
- 2016多校联赛4E (hdu5768) Lucky7
- 第三十二章 字符串匹配
- lucene性能优化
- 十六、迭代器模式Iterator(行为型)
- 全平台自动化测试软件的未来王者--上帝之眼( Sikuli )的简介与安装
- 同步容器&并发下的容器
- Linux目录结构和常用命令
- 如何使用CSDN-markdown编辑器
- Python(Dict和Set类型)
- Sun JVM 年轻代和老年代垃圾回收都需要暂停JVM