1. 什么是BM算法?
见 A Fast String Searching Algorithm, with R.S. Boyer. Communications of the Association for Computing Machinery,
是Boyer于1977年发表的一个算法,用于字符串匹配。
2. 与KMP算法相比?
与经典的KMP算法相比,BM算法在很多情况下效率更高,它有两个特点:
一是它在遍历正文时的平均比较次数与pattern的长度反比,这一点很历害。
二是它在处理大字母表时候性能更佳。
从表面上看KMP算法号称O(m+n)的时间复杂度,BM最坏为O(m*n),但实际效率反而是BM更高(见参考3),这是由于实际情况时经常能达到BM的平均效率。所以在内核的iptables匹配时,选择bm算法的时候居多!
3. 核心
BM算法的核心是两个原则,这在下面的参考2中可以找到,具体原理它已经说的很清楚了。
一。 坏字符原则
i. 如果字符x在模式P中没有出现,那么从字符x开始的m个文本显然不可能与P匹配成功,直接全部跳过该区域即可。
ii. 如果x在模式P中出现,则以该字符进行对齐。
二。 好后缀原则
i. 如果在P中位置t处已匹配部分P'在P中的某位置t'也出现,且位置t'的前一个字符与位置t的前一个字符不相同,则将P右移使t'对应t方才的所在的位置。
ii. 如果在P中任何位置已匹配部分P'都没有再出现,则找到与P'的后缀P''相同的P的最长前缀x,向右移动P,使x对应方才P''后缀所在的位置。
4. 个人的一点理解
需要说明的是原则一是核心,原则二是对原则一的补充优化;换句话说,仅仅利用原则一就能在正文中完成匹配,但如果结合原则二可以很好地优化某些情况,从而提高效率!
还有一点,如果只使用原则一,那么需要注意一个地方。BM算法的原论文里也忽视了这一点,比如下面这种情况:
按原论文的意思,这个时候c与X(蓝绿色部分)产生了坏字符,于是根据坏字符原则,下一次对齐的地方是"patter中的最右边的X"与text中的当前坏字符X, 那么这就产生了回退--pattern相对于text向左移动。要消除这个bug,就要在应用原则一的时候判断,不能让pattern左移!即一旦出现图中这种情况:
当前坏字符在pattern虽然有匹配,但由于匹配的位置在当前坏字符位置右边,于是相当于没有匹配,同样视为坏字符原则的第 i 种情况
。
之所以原论文会有这种疏忽,我觉得是因为原文的意思是在两个原则结合进行的前提下,上面提到的这种疏忽完全可以被原则二--好后缀原则会弥补这个bug ! 但严谨地讲来,这确实是一个不对的地方!
5. SNORT2.7.0中BM算法的一点疑问
在参考2中提到了SNORT2.7.0的算法,但个人觉得原文中的MakeSkip()函数有些问题:
原文中的:
应该改为:
否则,在这面这种情况下,匹配虽然能成功,但会多匹配 一次,,,可能是作者一个小疏忽:
6. 最后附上自己修改过的源代码,同时还有一些自己测试用的打印代码,主要是为了观察方便
参考:
1. BM算法的原论文
http://userweb.cs.utexas.edu/users/moore/publications/fstrpos.pdf
2.网上资料虽多,准确很少,这是个不错的原创:
原理说明:
http://www.javaeye.com/topic/352954
算法的C代码(个人觉得有个疑点,在上面5中有说明):
http://ouyangjia7.javaeye.com/blog/353137
3.关于BM算法的一点改进: