字符串匹配算法(转自http://apps.hi.baidu.com/share/detail/28862540)

来源:互联网 发布:简单的php网页 编辑:程序博客网 时间:2024/05/01 15:49

当问题的有效子串只有一个的时候,用KMP:给出1个单词,再给出一段包含m个字符的文章,让你找出这个单词是否在文章里出现过。

有效子串有一大堆的时候,可以用AC自动机(Aho-Corasick automation):给出n个单词,再给出一段包含m个字符的文章,让你找出有多少个单词在文章里出现过。

比较新的,还有一个Sunday算法。比较简单,而且快。

同时,这里有一篇相关介绍论文《一种可做特殊用途的字符串匹配算法》(纪福全 朱战立),可供参考:

字符串匹配就是在一个字符串中查找模式串的一个或所有出现。字符串匹配用途很广泛。例如,在拼写检查、语言翻译、数据压缩、搜索引擎、网络入侵检测、计算机病毒特征码匹配以及DNA序列匹配等应用中,都需要进行字符串匹配。已经提出了许多字符串匹配算法。传统的有BF算法[2,3]、KMP算法[2,3]等,最近提出的有BM算法[2,4]、Sunday算法[2,3]等。

相关算法分析    字符串模式匹配的含义是:在主串S中,从位置start开始查找是否存在模式串(也称作模式串)T,如在主串S中查找到一个与模式串T相同的模式串,则模式串与主串匹配;如在主串S中未查找到一个与模式串T相同的模式串,则不匹配 [1] 。首先作如下假设:

主串S:S[1…N],长度为N;模式串T:T[1…M],长度为M;N≥M;

BF算法     BF(Brute Force)算法核心思想是:首先S[1]和T[1]比较,若相等,则再比较S[2]和T[2],一直到T[M]为止;若S[1]和P[1]不等,则T向右移动一个字符的位置,再依次进行比较。如果存在k,1≤k≤N,且S[k+1…k+M]=T[1…M],则匹配成功;否则失败。该算法最坏情况下要进行M*(N-M+1)次比较,时间复杂度为O(M*N)。

KMP算法    KMP(Knuth-Morris-Pratt)算法核心思想是:在发生失配时,主串不需要回溯,而是利用已经得到的“部分匹配”结果将模式串右移尽可能远的距离,继续进行比较。这里要强调的是,模式串不一定向右移动一个字符的位置,右移也不一定必须从模式串起点处重新试匹配,即模式串一次可以右移多个字符的位置,右移后可以从模式串起点后的某处开始试匹配。
假设发生失配时,S[i]≠T[j],1≤i≤N,1≤j≤M。则下一轮比较时,S[i]应与T[next[j]]对齐往后比较:

如T=“abaabcac”,则

BM算法    对于给出的长度为m的模式串T=T&&05;···Tm和主串S=S&&05;···Sn,实现BM算法需要一个辅助数组bm[ ]。它用字符值作为数组的下标,数组的大小依赖于可能出现的字符多少,与模式串的大小无关。对于需要进行中文关键字的匹配,需要扩充ASCII字符集,数组大小则为256。对于任意x属于集合Σ→{1,2,···,256},bm[x]的值为:

该数组每个字符对应的项记录着该字符在模式串中最后一次出现的位置。BM算法思想是:假如在执行主串位置i起“返前”的一段与模式串T从右向左的字符匹配中,如果模式串T全部字符匹配,则匹配成功;否则需要右移,开始新的一轮匹配,假设匹配不成功发生在模式串中的位置j,由主串匹配不成功字符Si-m+j查找辅助数组得到该字符在模式串T中的最后出现的位置值bm[Si-m+j]。如果bm[Si-m+j]等于零,表示字符Si-m+j不在模式串T中,则模式串跳过该字符,在该字符下一个位置对齐;如果bm[Si-m+j]大于j,表示这个字符在模式串中最后出现的位置在j的左边,则模式串T右移对齐字符Si-m+j;如果bm[Si-m+j]小于j,表示这个字符在模式串中最后出现的位置在j的右边,模式串不能左移,就右移一格。移动量为shift=max(1,m-bm[i-m+j])。

Sunday算法    Sunday算法是Daniel M.Sunday于1990年提出的一种比BM算法搜索速度更快的算法。其核心思想是:在匹配过程中,模式串并不被要求一定要按从左向右进行比较还是从右向左进行比较,它在发现不匹配时,算法能跳过尽可能多的字符以进行下一步的匹配,从而提高了匹配效率。
假设在发生不匹配时S[i]≠T[j],1≤i≤N,1≤j≤M。此时已经匹配的部分为u,并假设字符串u的长度为L。如图1。明显的,S[L+i+1]肯定要参加下一轮的匹配,并且T[M]至少要移动到这个位置(即模式串T至少向右移动一个字符的位置)。

图1 Sunday算法不匹配的情况    分如下两种情况:
(1) S[L+i+1]在模式串T中没有出现。这个时候模式串T[0]移动到S[T+i+1]之后的字符的位置。如图2。

图2 Sunday算法移动的第1种情况
(2)S[L+i+1]在模式串中出现。这里S[L+i+1]从模式串T的右侧,即按T[M-1]、T[M-2]、…T[0]的次序查找。如果发现S[L+i+1]和T中的某个字符相同,则记下这个位置,记为k,1≤k≤M,且T[k]=S[L+i+1]。此时,应该把模式串T向右移动M-k个字符的位置,即移动到T[k]和S[L+i+1]对齐的位置。如图3。

图3 Sunday算法移动的第2种情况
依次类推,如果完全匹配了,则匹配成功;否则,再进行下一轮的移动,直到主串S的最右端结束。该算法最坏情况下的时间复杂度为O(N*M)。对于短模式串的匹配问题,该算法执行速度较快。

实验结果     为了评测该算法的性能,随机的抽取一段文本和模式串,并在同一台计算机上用不同的算法进行匹配。测试文本主串S="From automated teller machines and atomic clocks to mammograms and semiconductors,innumerable products and services rely in some way on technology,measurement,and standards provided by the National Institute of Standards and Technology",模式串T="products and services"。分别用BF算法、KMP算法、BM算法、Sunday算法和ZZL算法在同一台计算机上进行匹配计算,并统计每种算法匹配时总的字符匹配次数。测试结果如表1。
表1 匹配算法实验结果算法:
                                                BF KMP BM Sunday ZZL 
一次匹配的总的字符匹配次数 116 95    108 110         23

ZZL是该文作者提出的一个算法:先预处理查找模式串首字符在主串中的所有出现位置,并将其保存在一个数组中。字符串匹配算法就可以从查找到的模式串在主串中的位置开始,匹配模式串首字母之后的其余部分。此时,采用BF算法即可,并可设置一个计数器,记录匹配次数。对于频繁使用的要匹配的主串和模式串来说,由于预先保存了模式串在主串中的所有存储位置,所以ZZL算法的匹配速度会非常快。(前提是已经进行过预处理)