再谈KMP/BM算法(I)
之前我的《BM算法详解》一文中有一个巨大的缺憾,就是没能给出计算模式串好后缀跳转表的高效算法。Robert S.Boyer和J Strother Moore两人的论文中,不知什么原因,并没有给出这样的算法,蛮力算法O(n^3)的时间复杂度使得BM算法的实用性大打折扣。实际上线性时间内计算出模式串的好后缀跳转表的算法是存在,但是在介绍这个算法之前,我要向大家推荐一本字符串处理方面的权威著作《Algorithms on Strings,Trees and Sequences》,作者Dan Gusfield。书中几乎涵盖了当今具有实用价值的所有字符串处理技术,当然BM和KMP算法也涵盖其中,本文的内容就源于此书。不过这本书的内容可以说是非常非常的难,要想全部吃透十分不易。
在我的有关KMP,BM算法的两篇文章中,我已经提到了一个关键的问题,那就是前/后缀的自包含问题。无论是KMP算法还是BM算法的跳转表,都与自包含前/后缀有着直接的联系。这里我们需要引入一个概念Zi(S),其中S代表模式串,对于模式串S[1...n],Zi(S)表示子串S[i...j]的长度,其中j是所有满足S[i...j]=S[1...j-i+1]的j中的最大者。说起来挺玄乎,实际就是以i为起始的最长包含前缀。对于S=aabcaabxaaz,我们有
- Z5(S)=3,(aab)c(aab)xaaz
- Z6(S)=1,(a)abca(a)baaz
- Z7(S)=Z8(S)=0,当S[i]!=S[1]时Zi(S)=0
- Z9(S)=2,(aa)bcaabx(aa)z
由上面Z5(S)=3我们知道S[5...7]=S[1...3],且S[5...8]!=S[1...4],这里我们把S[5...7]叫做字符串S的一个Z-block,对于Zi(S),如果Zi(S)!=0,那么所标记的Z-block起始于i,结束于i+Zi(S)-1。显然,一个字符串可能包含若干个Z-block,而且各Z-block之间可能互相交叠。我们再定义两个值li,ri,其中li,ri是包含S[i]的所有Z-block中右端点最大的一个,如下图所示,这里包含i的Z-block有两个,只有标注a的Z-block的l值和r值,才是li和ri的实际值。实际上S[li...ri]=S[1...ri-li+1]。
现在我们就来介绍一下,在Z1(S),……,Zi(S),li,ri已知的情况下,如何求解Zi+1(S),这里我们令li=l, ri=r, i+1=k, i-li+2=k'。
1. 如果k,Zk'(S)与l,r的所决定的Z-block关系如下图所示,因为S[l...r]=S[1...r-l+1],所以我们可以把S[l...r]区间内的问题,放到S[1...r-l+1]区间内来考虑,此时k在1,r-l+1区间内的对应点就是k'。我们需要关注Zk'(S)这个已知量,在下图所示的这种情况中,Zk'(S)所决定Z-block完全包含在1,r-l+1区间内。也就是k'+Zk'(S)-1<r-l+1,此时Zk(S)实际上就等于Zk'(S)。
2. 如果k,Zk'(S)与l,r的所决定的Z-block关系如下图所示。此时,我们也同样将S[l...r]区间内的问题,放到S[1...r-l+1]区间内来分析。此时Zk'(S)所决定的Z-block的右端要超过r-l+1,也就是说对于Zk(S),我们已经知道其前r-k+1个元素与S[1...r-k+1]相同,但是对于S[r]以后的元素是否还可以与前面的r-k+1个元素连起来形成更长的包含前缀我们还只有进行比较后才能知道。由于之前我们的已经有S[k...r]=S[k'...r-l+1]=S[1...r-k+1](注意图中几个标注beta的区域),所以我们可以省去对这两个区间的比较,直接从S[r-k+2]开始与S[r-k+2]进行比较,直到匹配失败为止,此时我们就得到新的右端点ri+1,同时将li+1更新为i+1。
3. 如果r<=k。那么之前计算出的Z-block对我们没有任何帮助,我们从r开始,找到最小的k,使得S[r...k]!=S[1...r-k+1]。此时我们同时还要更新对应的li+1=i+1,ri+1=k-1。
分别处理上述三种情况,我们就可以在线性时间内,递推填写S[1...n]的所有Zi(S)值。假设模式串S="aabaabcaxaabaabcy",其对应的Zi(S)值如下表。
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | |
S | a | a | b | a | a | b | c | a | x | a | a | b | a | a | b | c | y |
Zi(S) | 0 | 1 | 0 | 3 | 1 | 0 | 0 | 1 | 0 | 6 | 1 | 0 | 3 | 1 | 0 | 0 | 0 |
当我们要计算Z12(S)时,Z1(S)到Z11(S)都已经计算得到,此时的l=10,r=15,也就是说S[10...15]所形成的Z-block是当前的最右Z-block且包含S[12]。此时我们要计算Z12(S),由于S[10...15]=S[1...6],所以Z12(S)与Z3(S)密切相关,我们发现Z3(S)=0,3+Z3(S)=3<6,这个符合前面的第一种情况,所以Z12(S)=Z3(S)=0.
对于Z10(S),当计算Z10(S)时,已知的最右Z-block是S[8],l=8,r=8,因为10>8,所以符合上述第三种情况,我们直接从S[10]开始向后寻找S的包含前缀,找到S[10...15]是一个长度为6的S的包含前缀,所以Z10(S)=6,同时更新l=10,r=15.
在Zi(S)值计算中,第二种情况的场景比较少见,但是第二种情况也是Zi(S)计算中最容易出问题的部分。
下面给出我自己写的计算Z数组的算法
- void ZBlock(const char* pattern, unsigned int length, unsigned int zvalues[])
- {
- unsigned int i, j, k;
- unsigned int l, r;
- l = r = 0;
- zvalues[0] = 0;
- for(i = 1; i < length; ++i)
- {
- if(i >= r)
- {
- j = 0;
- k = i;
- zvalues[i] = 0;
- while(k < length && pattern[j] == pattern[k])
- {
- ++j;
- ++k;
- }
- if(k != i)
- {
- l = i;
- r = k - 1;
- zvalues[i] = k - i;
- }
- }
- else
- {
- if(zvalues[i - l] >= r - i + 1)
- {
- j = r - i + 1;
- k = r + 1;
- while(k < length && pattern[j] == pattern[k])
- {
- ++j;
- ++k;
- }
- l = i;
- r = k - 1;
- zvalues[i] = k - i;
- }
- else
- {
- zvalues[i] = zvalues[i - l];
- }
- }
- }
- }
因为普通的字符串是从索引0开始,所以算法中对此作了调整。
Z-block算法从理论上彻底解决了前缀自包含的计算问题,从易理解的角度上讲,Z-block算法也要明显优于KMP算法中三人对next表构造过程的描述。拥有了模式串的Z值数组后,相应的KMP算法的next跳转表,BM算法的好后缀表的计算都将变得高效,直观。
再谈KMP/BM算法(I)相关推荐
- 【字符串算法1】 再谈字符串Hash(优雅的暴力)
[字符串算法1] 字符串Hash(优雅的暴力) [字符串算法2]Manacher算法 [字符串算法3]KMP算法 这里将讲述 [字符串算法1] 字符串Hash 老版原文: RK哈希(Rabin_Ka ...
- 数据结构与算法--再谈递归与循环(斐波那契数列)
再谈递归与循环 在某些算法中,可能需要重复计算相同的问题,通常我们可以选择用递归或者循环两种方法.递归是一个函数内部的调用这个函数自身.循环则是通过设置计算的初始值以及终止条件,在一个范围内重复运算. ...
- 经典算法研究系列:八、再谈启发式搜索算法
经典算法研究系列:八.再谈启发式搜索算法 作者:July 二零一一年二月十日 本文参考: I. 维基百科. II. 人工智能-09 启发式搜索. III.本BLOG内,经典算法研究系列:一.A ...
- 【徒手写机器学习算法】再谈数据源:从普通图片到Cifar-10(使用C++)
[徒手写机器学习算法]再谈数据源:从普通图片到Cifar-10(使用C++) 在本系列的第一篇文章里,关于机器学习的数据源的问题被一笔带过(使用csv格式的数据),这一篇文章我会给出关于图片数据制作的 ...
- 再谈GC1:GC简介,分代与回收算法
说明:在本文中, Garbage Collection 翻译为 "垃圾收集", garbage collector 翻译为 "垃圾收集器";一般认为, 垃圾回收 ...
- 字符串匹配-BM算法改进SUNDAY--Boyer-Moore-Horspool-Sunday Aglorithm
原文:http://blog.csdn.net/zhoubl668/article/details/7321271 BM算法的改进的算法SUNDAY--Boyer-Moore-Horspool-Sun ...
- 分享一下字符串匹配BM算法学习心得。
字符串匹配BM(Boyer-Moore)算法学习心得 BM算法 是 Boyer-Moore算法 的缩写,是一种基于后缀比较的模式串匹配算法.BM算法在最坏情况下可以做到线性的,平均情况下是亚线性的(即 ...
- 字符串匹配算法(三):KMP(KnuthMorrisPratt)算法
文章目录 KMP 原理 next数组的构建 代码实现 KMP 一提到字符串匹配算法,想必大家脑海中想到的第一个必然就是KMP算法,KMP算法的全称叫做KnuthMorrisPratt算法,与上一篇博客 ...
- 【数据结构与算法】字符串匹配 BM算法
单模式串匹配 BF 算法和 RK 算法 BM 算法和 KMP 算法 多模式串匹配算法 Trie 树和 AC 自动机 BM算法 BM算法的核心思想是通过将模式串沿着主串大踏步的向后滑动,从而大大减少比较 ...
- diff算法阮一峰_【重学数据结构与算法(JS)】字符串匹配算法(三)——BM算法
前言 文章的一开头,还是要强调下字符串匹配的思路 将模式串和主串进行比较 从前往后比较 从后往前比较 2. 匹配时,比较主串和模式串的下一个位置 3. 失配时, 在模式串中寻找一个合适的位置 如果找到 ...
最新文章
- 【Network Security!】虚拟化架构与系统部署
- 如何屏蔽PHP浏览器头信息X-Powered-By
- -又见GCD -- ACM解决方法
- 小白学jquery Mobile《构建跨平台APP:jQuery Mobile移动应用实战》连载四(场景切换)...
- ubuntu 安装PCL
- 【计算机科学基础】计算机不需要整数减法器的原因
- CCNET自动构建之路
- 金蝶云星空使用WebAPI来新增单据
- vue 添加子路由,并对路由重定向
- mt4双线macd_手机版MT4怎样添加双线MACD指标 手机MT4双线MACD设置方法
- VK Cup 2018 Round 1: A. Primal Sport
- matlab 韩明距离_科学网—Matlab中 pdist 函数详解(各种距离的生成) - 朱新宇的博文...
- WinRAR 5.60 官方简体中文无广告弹窗版本
- WORD插入多张图片并上下左右居中自动对齐操作;论文图片表格排版
- 那些年收藏的技术文章(二)-云笔记篇
- html怎么做出相框的效果,使用CSS3制作PS级的图片边框效果
- React Native 实践之携程 Moles 框架
- Linux安装mysql没有my.cnf文件
- 【文献解读 情感合成】Expressive Speech Driven Talking Avatar Synthesis with DBLSTM using 有限的情感双峰数据
- C# StatusBar
热门文章
- 【读书笔记《Bootstrap 实战》】6.单页营销网站
- 最短路径例题(Floyd、Dijkstra)
- vue+element+node构建单片机控制系统
- hql中 oracle当前时间,hql oracle 比较 日期时间
- mysql in range_mysql 的 RANGE 分区有价值吗?
- Linux下更改Python的软链接
- Python - PyCharm部分快捷键
- r语言集合补集_极速统计教程之八 | 概率和集合
- html是用来表示网上信息的符号标记语言,html标记的一般格式
- mongodb创建local库用户_MongoDB 在系统数据库local上无法创建用户的解决方法