字符串匹配:
---willamette
在匹配串中寻找模式串是否出现,注意和最长公共子序列相区别(LCS: Longest Common Substring)
最简单的Brute Force算法:
首先将匹配串和模式串左对齐,然后从左向右一个一个进行比较,如果不成功则模式串向右移动一个单位。
速度最慢。
那么,怎么改进呢?
我们注意到Brute Force算法是每次移动一个单位,一个一个单位移动显然太慢,是不是可以找到一些办法,让每次能够让模式串多移动一些位置呢?
当然是可以的。
我们也注意到,Brute Force是很不intelligent的,每次匹配不成功的时候,前面匹配成功的信息都被当作废物丢弃了,当然,就如现在的变废为宝一样,我们也同样可以将前面匹配成功的信息利用起来,极大地减少计算机的处理时间,节省成本。^_^
首先介绍的就是KMP算法。
原始论文:Knuth D.E., Morris J.H., and Pratt V.R., Fast pattern matching in strings, SIAM Journal on Computing, 6(2), 323-350, 1977.
这个算法实在是太有名了,大学上的算法课程除了最笨的Brute Force算法,然后就介绍了KMP算法。也难怪,呵呵。谁让Knuth D.E.这么world famous呢,不仅拿了图灵奖,而且还写出了计算机界的Bible <The Art of Computer Programming>(业内人士一般简称TAOCP).稍稍提一下,有个叫H.A.Simon的家伙,不仅拿了Turing Award,顺手拿了个Nobel Economics Award,做了AI的爸爸,还是Chicago Univ的Politics PhD,可谓全才。
KMP的思想是这样的:
利用不匹配字符的前面那一段字符的最长前后缀来尽可能地跳过最大的距离
比如
模式串ababac这个时候我们发现在c处不匹配,然后我们看c前面那串字符串的最大相等前后缀,然后再来移动
下面的两个都是模式串,没有写出来匹配串
原始位置ababac
移动之后 ababac
因为后缀是已经匹配了的,而前缀和后缀是相等的,所以直接把前缀移动到原来后缀处,再从原来的c处,也就是现在的第二个b处进行比较。这就是KMP。
当然,有市场就有竞争,字符串匹配这么大一个市场,不可能让BF和KMP全部占了,于是又出现了几个强劲的对手。
第一个登场的是Horspool算法。
论文:Horspool R.N., 1980, Practical fast searching in strings, Software - Practice & Experience, 10(6):501-506
Horspool算法的思想很简单的。不过有个创新之处就是模式串是从右向左进行比较的。很好很强大,为后来的算法影响很大。
匹配串:abcbcsdxzcxx
模式串:cbcac
这个时候我们从右向左进行对暗号,c-c,恩对上了,第二个b-a,不对啊,我们应该怎么办?难道就这么放弃么。于是,模式串从不匹配的那个字符开始从右向左寻找匹配串中不匹配的字符b的位置,结果发现居然有,赶快对上赶快对上,别耽误了。
匹配串:abcbcsdxzcxx
模式串: cbcac
然后继续从最右边的字符从右向左进行比较。这时候,我们发现了,d-c不匹配啊,而且模式穿里面没有噢,没办法,只好移动一个模式串长度的单位了。
匹配串:abcbcsdxzcxx
模式串: cbcac
第二个上来的是Boyer-Moore算法。
是一个很复杂的算法,当然,虽然理论上时间复杂度和KMP差不多,但是实际上却比KMP快数倍,可见实践是检验真理的唯一标准。
原始论文:R.S.Boyer, J.S.Moore, A fast string searching algorithm , Communications of the ACM,20(10):762-772 ,1977
分为两步预处理,第一个是bad-character heuristics,也就是当出现错误匹配的时候,移位,基本上就是做的Horspool那一套。
第二个就是good-suffix heuristics,当出现错误匹配的时候,我还要从不匹配点向左看啊,以前匹配的那段子字符串是不是在模式串本身中还有重复的啊,有重复的话,那么我就直接把重复的那段和匹配串中已经匹配的那一段对齐就是了。再比较
匹配串:abaccbabbazz
模式串:cbadcba
我们看到已经匹配好了cba,但是c-d不匹配,这个时候我们发现既可以采用bad-character heuristics,也可以使用good-suffix heuristics(模式串:cbadcba),在这种情况下,邪不压正。毅然投奔good。移动得到
匹配串:abaccbabbazz
模式串: cbadcba
可是,我们有时候也发现,已经匹配好的那一部分其实并没有再有重复了的啊。这个时候,我们发现已经匹配好的那串字符串有一部分在开头重新出现了,那么,赶快,对齐吧。
匹配串:abacccbbbazz
模式串:cbadccb
然后得到
匹配串:abacccbbbazz
模式串: cbadccb
当两种Good-Suffix出现的时候,取移动距离最大的那个。
最后一个是Sunday算法,实际上比Boyer-Moore还快,呵呵。长江后浪推前浪。
原始论文:Daniel M. Sunday, A very fast substring search algorithm, Communications of the ACM, v.33 n.8, p.132-142, Aug. 1990
看原始论文的题目,D.M. Sunday貌似是故意想气气Boyer-Moore两位大牛似的。呵呵。不过实际上的确Sunday算法的确比BM算法要快,而且更简单。
Sunday的算法思想和Horspool有些相似,但是。当出现不匹配的时候,却不是去找匹配串中不匹配的字符在模式串的位置,而是直接找最右边对齐的右一位的那个字符在模式串的位置。
比如:
匹配串:abcbczdxzc
模式串:zbcac
恩,这里我们看到b-a没有对上,我们就看匹配串中的z在模式串的位置,然后,嘿嘿。
匹配串:abcbczdxzc
模式串: zbcac
如果模式串中的没有那个字符怎么办呢?很简单,跳过去呗。
匹配串:abcbcedxzcs
模式串:zbcac
e不在模式串中出现
那么我们就
匹配串:abcbcedxzcs
模式串: zbcac
实际上,现在还有很多很多字符串匹配算法,这里只是简单介绍了一下最常使用的五种算法,更多算法可以参考一下http://www.inf.fh-flensburg.de/lang/algorithmen/algo.htm,8过这个是德文网站,有的网页没有英文版的哦。