ref : https://dsqiu.iteye.com/blog/1700312html
本文内容框架:ios
§1 Boyer-Moore算法web
§2 Horspool算法算法
§3 Sunday算法数组
§4 KMP算算法数据结构
§5 KR算法框架
§6 AC自动机ide
§7 小结函数
§1 Boyer-Moore(BM)算法性能
Boyer-Moore算法原理
Boyer-Moore算法是一种基于后缀匹配的模式串匹配算法,后缀匹配就是模式串从右到左开始比较,但模式串的移动仍是从左到右的。字符串匹配的关键就是模式串的如何移动才是最高效的,Boyer-Moore为了作到这点定义了两个规则:坏字符规则和好后缀规则,下面图解给出定义:
下面分别针对利用坏字符规则和好后缀规则移动模式串进行介绍:
坏字符规则
1.若是坏字符没有出如今模式字符中,则直接将模式串移动到坏字符的下一个字符:
(坏字符c,没有出现模式串P中,直接将P移动c的下一个位置)
2.若是坏字符出如今模式串中,则将模式串最靠近好后缀的坏字符(固然这个实现就有点繁琐)与母串的坏字符对齐:
(注:若是模式串P是babababab,则是将第二个b与母串的b对齐)
好后缀规则
好后缀规则分三种状况
1.模式串中有子串匹配上好后缀,此时移动模式串,让该子串和好后缀对齐便可,若是超过一个子串匹配上好后缀,则选择最靠靠近好后缀的子串对齐。
2.模式串中没有子串匹配上后后缀,此时须要寻找模式串的一个最长前缀,并让该前缀等于好后缀的后缀,寻找到该前缀后,让该前缀和好后缀对齐便可。
其实,1和2均可以当作模式串还含有好后缀串(好后缀子串也是好后缀)。
3.模式串中没有子串匹配上后后缀,而且在模式串中找不到最长前缀,让该前缀等于好后缀的后缀。此时,直接移动模式到好后缀的下一个字符。
Boyer-Moore算法步骤
1.对模式子串进行预处理
Boyer-Moore算法实现必须对模式串进行预处理,获得坏字符规则和好后缀规则移动的映射表,下面代码中MakeSkip是创建坏字符规则移动的映射表,MakeShift是创建好后缀规则的移动映射表。
MakeSkip是构造数组skip[],skip[k]表示字符k距离模式串末尾的距离。
MakeShfit是构造数组shfit[],shfit[k]表示模式串的以k为边界的后缀子串的最靠近的模式子串(或最前缀子串)到模式子串末尾的距离,例如:abcab,shfit[3]=3和shfit[2]=3(即都是第一个b到末尾的距离),k=2时,后缀子串为cab,这时只有最长前缀ab,shfit[2]=3。
2.从b_idx开始查找,获得坏字符和好后缀,获得最大移动距离,移动b_idx,直至b_idx到达母串的末尾。
Boyer-Moore算法实现
算法的时间复杂度最差(匹配不上)是O(n×m),最好是O(n),其中n为母串的长度,m为模式串的长度。BM算法时间复杂度最好是O(n/(m+1))
§2 Horspool算法
horspool算法将主串中匹配窗口的最后一个字符跟模式串中的最后一个字符比较。若是相等,继续从后向前对主串和模式串进行比较,直到彻底相等或者在某个字符处不匹配为止(以下图中的α与σ失配) 。若是不匹配,则根据主串匹配窗口中的最后一个字符β在模式串中的下一个出现位置将窗口向右移动。
Horspool算法相对于Boyer-Moore算法改进了坏字符规则,Boyer-Moore算法只是将模式串P中从当前未匹配位置向右第一个坏字符与母串的坏字符(未匹配的字符)对齐进行再次匹配,Horspool算法是以当前匹配窗口中母串的最末尾的一个字符和模式串最靠近它的字符对齐,下图中β是当前匹配窗口的母串最后一个字符,将其与模式串左边最靠近的β对齐移动。
Horspool算法预处理
为了实现模式串的移动,必须先记录每个字符串在模式串中距离最右边的距离:
Horspool算法实现
Horspool算法时间复杂度
假设主串的长度为n,模式串的长度为m,那么Horspool算法最坏状况下的时间复杂度是O(mn),但平均状况下它的时间复杂度是O(n)。
§3 Sunday算法
Sunday算法思想跟BM算法很类似,在匹配失败时关注的是文本串中参加匹配的最末位字符的下一位字符。若是该字符没有在匹配串中出现则直接跳过,即移动步长= 匹配串长度+1;不然,同BM算法同样其移动步长=匹配串中最右端的该字符到末尾的距离+1。
Sunday算法实现
Boyer-Moore、Horspool、Sunday算法小结
Boyer-Moore、Horspool、Sunday算法都是基于后缀数组的匹配算法,区别在于移动的方式不同(好像网上有些都没有说的Boyer-Moore算法的好后缀规则,有多是优化方法吧,没有去深究,抱歉)。下面给出三种方法的对比:
In this example, t0, ..., t4 = a b c a b is the current text window that is compared with the pattern. Its suffix a b has matched, but the comparison c-a causes a mismatch. The bad-character heuristics of the Boyer-Moore algorithm (a) uses the "bad" text character c to determine the shift distance. The Horspool algorithm (b) uses the rightmost character b of the current text window. The Sunday algorithm (c) uses the character directly right of the text window, namely d in this example. Since d does not occur in the pattern at all, the pattern can be shifted past this position.
§4 Knuth-Morris-Pratt(KMP)算法
KMP算法是一种高效的前缀匹配算法,在传统蛮力(BF)匹配算法的基础上改进的地方在于每次移动的距离不是1能够是更大,没有进行回溯,BF算法的时间复杂度是O(m*n),而KMP算法的时间复杂度是O(m+n)。
假设执行第i+1趟匹配时,若是比较模式串P中的第j个字符时不匹配,也就是有
T[i,i+1,...,i+j-1]=P[0,1,...,j-1],T[i+j]≠P[j] (打不了下标,就有数组的形式给出字符串) (1)
BF算法下一趟是从目标的第i+1位置开始与模式串比较。若是匹配成功则有
T[i+1,i+2,...,i+m]=P[0,1,...m-1] (2)
若是模式串P有以下特征
P[0,1,...j-2]=P[1,2,...j-1] (3)
由(1)可知
T[i+1,i+2,...,i+j+1]=P[1,2,...j-1] (4)
由(3)(4)可知
T[i+1,i+2,...,i+j+1]≠P[0,1,...j-2] (5)
故由
T[i+1,i+2,....,i+m]≠P[0,1,...m-1]
因此第i+2趟是匹配能够不须要进行,由于必定不能匹配。
相似能够推得
P[0,1,...k-1]=P[j-k-1,j-k,...j-1]
这时才有
P[0,1,...k-1]=P[j-k-1,j-k,...j-1]=T[i+j-k,i+j-k+1,i+j-1]
模式串P从当前位置直接向右移动 j-k 位置,使模式串P的第 k 个字符P[k]与目标串T中的第i+j个字符对齐开始比较(前面 k 个已经匹配)。
形成BF算法效率低的主要缘由是在算法执行过程当中有回溯,而这些回溯是能够避免的。KMP算法的关键是在匹配失败时,肯定下一次匹配的位置,设next[j]=k,表示当模式串P中第j个字符与母串T相应字符不匹配时,模式串P中应当由第K个字符与目标串中刚不匹配的字符对齐继续进行比较。
例如,模式串P="abaabcac",其对应的next[j]以下:
i |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
t[i] |
a |
b |
d |
a |
b |
c |
d |
e |
next[i] |
-1 |
0 |
0 |
0 |
1 |
2 |
0 |
0 |
next数组构造
╔ -1, j=0;
next[j]= ║max{k| 0<k<j 且 P[0,1,...,k-1]=P[j-k,j-k+1,..j-1}
╚ 0, 其余状况
next数组求解是一个递推过程,
设next[j]=k,则有
P[0,1,...k-1]=P[j-k,j-k+1,...,j-1]
next[j]= ╔ max{k| 0<k<j 且 P[0,1,...,k]=P[j-k,j-k+1,..j-1}
╚ 0, 其余状况
若是P[k]=P[j],有 next[j+1]=next[j]+1=k+1。
若是P[k]≠P[j],有 P[0,1,...,k]≠P[j-k,j-k+1,...j],
假设next[j+1]=h+1,则有下式成立
P[0,1,...h]=P[j-h+1,j-k+1,...j] P[h]=P[j]
又由于
P[0,1,...h-1]=P[j-h,j-k+1,...j-1]=P[k-h,k-h+1,k-1] (next[k]=h的状况)
即此时实际只须要知足 next[k]=h(前面已经求解过)时,P[h]=P[j] 就有next[j+1]=h+1,不然(不存在这样的h)next[j+1]等于0。
由此能够获得计算next的递推公式
KMP算法实现
§5 Karp-Rabin(KR)算法
Karp-Rabin算法是利用hash函数的特性进行字符串匹配的。 KR算法对模式串和循环中每一次要匹配的子串按必定的hash函数求值,若是hash值相同,才进一步比较这两个串是否真正相等。
Karp-Rabin算法适用于多个字符串匹配较好。
§6 Aho-Corasick算法
Aho-Corasick算法又叫AC自动机算法,是一种多模式匹配算法。Aho-Corasick算法能够在目标串查找多个模式串,出现次数以及出现的位置。
Aho-Corasick算法原理
Aho-Corasick算法主要是应用有限自动机的状态转移来模拟字符的比较,下面对有限状态机作几点说明:
上图是由多模式串{he,she,his,hers}构成的一个有限状态机:
1.该状态当字符匹配是按实线标注的状态进行转换,当全部实线路径都不知足(即下一个字符都不匹配时)按虚线状态进行转换。
2.对ushers匹配过程以下图所示:
当转移到红色结点时表示已经匹配而且得到模式串
Aho-Corasick算法步骤
Aho-Corasick算法和前面的算法同样都要对模式串进行预处理,预处理主要包括字典树Tire的构造,构建状态转移表(goto),失效函数(failure function),输出表(Output)。
Aho-Corasick算法包括如下3个步骤
1.构建字典树Tire
2.构建状态转移表,失效函数(failure function),输出表(Output)
3.搜索路径(进行匹配)
下面3个步骤分别进行介绍
构建字典树Tire
Tire是哈希树的变种,Tire树的边是模式串的字符,结点就是Tire的状态表,下图是多模式串{he,she,his,hers}的Tire树结构:
构建goto函数、failure function和Output函数
goto函数(状态转移函数):goto(pre,v)=next,完成这样的任务:在当前状态pre,输入字符v,获得下一个状态next,若是没有下个状态则next=failure。
failure function:失效函数是处理当前状态是failure时的处理。
output函数:当完成匹配是根据状态输出匹配的模式串。
下面是多模式串{he,she,his,hers}的goto函数,failure函数,output函数
goto函数:
failure函数
output函数
多模式串{he,she,his,hers}最终的有限状态机图
Aho-Corasick算法实现
§7 小结
这篇文章把字符串匹配的六个算法——BM、Horspool、Sunday、KMP、KR、AC算法,从原理到步骤,再从流程到实现都作了讲解了,能有了必定的认识和理解,基本能够掌握这些算法。若是你有任何建议或者批评和补充,请留言指出,不胜感激,更多参考请移步互联网。
参考(后面3个连接很不错的哟):①jijy:http://www.searchtb.com/2011/07/%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%8C%B9%E9%85%8D%E9%82%A3%E4%BA%9B%E4%BA%8B%EF%BC%88%E4%B8%80%EF%BC%89.html②ouyangjia7:http://ouyangjia7.iteye.com/blog/353137③那谁的技术博客:http://www.cppblog.com/converse/archive/2006/07/05/9447.html④CobbLiu:http://www.cnblogs.com/cobbliu/archive/2012/05/29/2524244.html⑤kmplayer:http://kmplayer.iteye.com/blog/704187⑥志文工做室:http://www.zhiwenweb.cn/Category/Security/1274.htm⑦http://www.iti.fh-flensburg.de/lang/algorithmen/pattern/sundayen.htm⑧http://www-igm.univ-mlv.fr/~lecroq/string/⑨http://www.csie.ntnu.edu.tw/~u91029/StringMatching.html