KMP算法的next函数求解和分析过程
转自
wang0606120221:http://blog.csdn.net/wang0606120221/article/details/7402688
假设KMP算法中的模式串为P,主串为S,那么该算法中的核心是计算出模式串的P的next函数。
KMP算法是在已知的模式串的next函数值的基础上进行匹配的。
由于本次只讨论next的求值过程,因此KMP算法的数学推理过程这里不再讲解。
从KMP算法的数学推理可知,此next函数只取决与模式匹配串自身的特点和主串没有任何关系,此函数
默认认为next[1]=0,由于next[j]=k表示的意义是当模式串和主串的第j个字符不匹配时,那么接下来和主串的第j个
字符匹配的字符是模式串的第k个字符。因此,next[1]=0表示当主串的当前字符和模式串的第1个字符不匹配,接
下来需要用模式串的第0个字符和主串的当前字符匹配,由于模式串下标是从1开始的,所以不可能存在第0个字符,
即接下的匹配动作是主串和模式串同时向右移动一位,继续模式匹配。
例如:主串:a c a b a a b a a b n a c
模式串:a b a a b
主串:a c a b a a b a a b n a c
模式串: a b a a b
主串:a c a b a a b a a b n a c
模式串: a b a a b
此时,主串和模式串不匹配,而next[1]=0,因此,模式串的第0个字符和主串的第2个字符比较,而模式串没有第0个
字符,此时可以把第0个字符理解为空字符,即模式串向右移动一位,主串再继续喝模式串匹配,而此时的主串的当
前字符是第3个字符,整体来看是当主串和模式串的第1个字符不匹配时,主串和模式串同时右移一位,然后继续匹配。
接下来讲解一般情况下的next函数值求解过程。
设next[j]=k,根据KMP算法的模式串的特点可知,‘p1p2......pk-1’=‘pj-k+1......pj-1’,其中k必须满足1<k<j,并且不可能存在k‘>k满足上面等式。那么能够根据next[j]=k计算出next[j+1]的值吗?显然是能够计算出来的,不然我也不废话了,呵呵。
下面讨论具体求解过程:
当知道next[j]=k的值,求next[j+1]的值时候有两种情况,一种是pk=pj,另一种情况是pk!=pj。
1.当pk=pj时,那么可以得出在模式串中存在‘p1......pk’=‘pj-k+1......pj’子串等式。并且不可能存在k‘>k,满足
"p1......pk’ "=" pj-k’+1......pj "等式(该等式中用的是双引号,只是为了区别外边字符串的引号和内部k‘的引号,没有其它意图,特此说明),因为根据KMP算法的说明k是最大的。
因此,显而易见next[j+1]=k+1=next[j]+1。该式表示当主串的当前字符和模式串的第j+1个字符不匹配时,需要使用模式串的第k+1个字符和当前主串字符比较匹配,即主串的当前字符不变,变的只是模式串的字符,可以理解为模式串向右移动j+1-(k+1)=j-k个字符。
2.当pk!=pj时,可知在模式串中不存在‘p1......pk’=‘pj-k+1......pj’等式。表明不能简单的用模式串的第k+1个字符和主串比较,因为pk!=pj,此时需要把模式串向右移动更多的位数,直到找出模式串的前m个字符和主串中的m个字符匹配为止或者没有找到这样的子串,那么意味着模式串需要从第1个字符开始和主串从新匹配。
主串:a c a b a a c a b a a b a a b n a c
模式串: a b a a b
主串:a c a b a a c a b a a b a a b n a c
模式串: a b a a b
在此首先给出了next函数的数值,为了方便说明当pk!=pj时,使用pk=pj的方法是不对的。
next[1]=0,next[2]=1,next[3]=1,next[4]=2,next[5]=2.
此时,i=7,j=5,而s[7]!=t[5],如果简单按照pk=pj时候的方法,用第k+1=2+1=3个字符和主串的第7个字符比较时,显然不行,因为s[6]=a!=b=t[2]。前面子串都不匹配,那么匹配后面的字符显然是笑话。说明此时模式串需要向右移动更多的位数,知道找到合适的字符或者没有找到,需要从第1个字符重新和主串匹配。
因为已经知道next[j]=k,因此可知模式串中p1=pj-k+1,p2=pj-k+2,......,pk-1=pj-1,则此时应该将模式串继续向右移动直到第m+1个字符,该字符满足’p1......pm‘=’pj-m+1......pj‘,(1<m<k<j)并且不存在m’>m也满足该等式。此时就可以使用第m+1个字符和当前主串字符比较匹配,(假设当前主串的字符索引是i+1)
此时主串中必存在关系式‘si-m+1......si’=‘pj-m+1......pj’=‘p1......pm’,因此用模式串的第m+1个字符和当前主串字符比较匹配是正确的。此时next[j+1]=m+1=next[......next[next[k]]]+1。
这里m=next[......next[next[k]]],这里解释一下m=next[......next[next[k]]],由于pk!=pj,因此需要向右移动模式串,
首先移动第next[k]个字符和第j个字符比较,假设next[k]=h,如果ph=pj,
则说明模式串中存在等式‘p1......ph’=‘p-h+1......pj’,(1<h<k<j)(假设主串中当前和模式串比较字符是第i+1个)则主串中必然存在‘si-h+1......si’=‘pj-h+1......pj’=‘p1......ph’等式(1<h<k<j)。也就是说next[j+1]=h+1。
即next[j+1]=next[k]+1。
同理,如果ph!=pj,则模式串还需要继续向右移动,用第next[h]个字符和第j个字符比较,以此类推,直到第j个字符和模式串中的某个字符匹配成功或者不存在u(1<u<j)满足等式‘p1......pu’=‘pj-u+1......pj’。如果找到了匹配字符u,那么
next[j+1]=u+1,否则next[j+1]=1。
下面举例说明该过程:next函数值只与模式串自身的特点有关;
模式串的索引:j 1 2 3 4 5 6 7 8
模式串: a b a a b c a c
next值: 0 1 1 2 2 3 1 2
默认设置next[1]=0;
计算next[2]:因为p1......p2-1,不可能存在索引k,满足1<k<2,因此next[2]=1;
计算next[3]:因为p3-1=b!=a=p1,next[2]=1,而next[1]=0,所以不存在u,满足上述表达式,next[3]=1;
计算next[4]:next[3]=1,p4-1=a=p1,next[4]=next[3]+1=1+1=2;
计算next[5]:next[4]=2,p2=b,p5-1=a!=p2,pj=p4=a,next[2]=1,p1=a=pj,所以next[5]=next[2]+1=1+1=2;
计算next[6]:next[5]=2,p2=b,p6-1=b=p2,next[6]=next[5]+1=2+1=3;
计算next[7]:next[6]=3,p3=a,p7-1=c!=p3,pj=p6=c,next[3]=1,p1=a!=pj,next[1]=0,因此不存在u,所以,next[7]=1;
计算next[8]:next[7]=1,p1=a,p8-1=a=p1,next[8]=next[7]+1=1+1=2。
为什么当模式串第j个字符和主串不匹配时接着用第next[k]个字符直接和第j个字符比较,而不是用第j-1个字符和主串比较呢?
我的证明过程如下。
证明:因为前提条件是next[j]=k,那么可以得知’p1.......pk-1‘=’p-k+1......pj-1‘,并且k满足1<k<j和不存在h,1<k<h<j,满足上面等式,即‘p1......ph-1’=‘pj-h+1......pj-1’,即此时的k值是最大的。
假设此时主串索引为i+1,模式串为j+1,那么‘p1......pj-1’=‘p2......pj’等式是肯定不能成立的。因为如果该等式成立,那么就会存在等式‘p1......pj-2’=‘p2......pj-1’成立,而根据next[j]=k的前提条件,我们已经得出不存在h,1<k<h,满足等式‘p1......ph-1’=‘pj-h+1......pj-1’,而此时却奇怪得出了存在j-1满足上面等式,和已知条件产生矛盾。
所以,next[k]和j之间的所有字符都是不能满足算法寻找的字符满足的等式的条件的。因此,如果pk!=pj,那么接下来必须用从0到next[k]之间的字符和第j个字符比较匹配,所以本算法首先采用从第next[j]个字符和第j个字符比较匹配。
如果我证明的不对,还请大牛们把正确答案告诉我一下,让我也得到正确的证明过程。谢谢!
终上所述,next的函数表达式如下所示:
0,j=1;
next[j]= MAX{k | ‘p1......pk-1’=‘pj-k+1......pj-1’,1<k<j},集合不为空;
1,其他情况。
假设next[j]=k。
next[j]+1=k+1,pj=pk;
next[j+1]=1,pk!=pj,不存在字符u,1<u<k,满足等式‘p1......pu’=‘pj-u+1......pj’;
next[......next[next[k]]]+1,pk!=pj,找到了u,1<u<k,‘p1......pu’=‘pj-u+1......pj’。
本博客主要讲的是如何按照地推的思想来计算出所有的next函数, 减少每次都扫描整个模式串计算next数值,通过递归算法可以利用前面已经计算出的next[j]的数值可以计算出next[j+1],这样可以提高不少效率。
KMP算法的求next数值函数代码如下:其中绿色的行代表代码,其它代表对每一行代码的注释。
void getNext(String p,int[] next)
{
//next初始化next[1]=0,由于需要计算最大k值,因此从第2个字符开始查找匹配子串,使得u满足1<u<j
//’p1......pu-1‘=’pj-u+1......pj-1‘,本算法使用了两个索引指针,i和j,并且初始化i=1,j=0。这里为了计算出next数 //值,主串和模式串都是使用的模式串数据。如
//主串: a b a a b c a c,i=1;
//模式串: a b a a b c a c,j=0.
int i=1; next[1]=0; j=0;
//线性扫描主串,主串不回朔,只能增加,而模式串可能会不断的向右滑动,寻找字符u,使得pu=pj,求解 //next[j+1]。
while(i<=p.length)
{
//由于模式串不存在第0个,因此j==0代表主串和模式串肯定不匹配,此时,主串和模式串都必须增加1个索引,然 //后继续匹配操作。此时j=0+1=1,next[i]=1和当主串和模式串的第0个字符比较得出的结果一致,即此时需要使用模式串的第1个字符和主串匹配比较。另外j==0条件还包含了一种情况是在模式串中找不到字符u,使得pu=pj,此时next[j]都等于1。
//p[i]=p[j]条件表示主串和模式串匹配,因此,主串和模式串都需要增加一个字符索引,假设此时主串索引为i和模式串索引为j,可以得出,’p1......pj‘=’pi-j+1......pi‘,由于i和j都要增加一个字符索引,此时主串为i=i+1,模式串为j=j+1,所以next[i]=j;
if(j==0||p[i]==p[j]) {++i;++j;next[i]=j;}
//当j!=0并且p[i]!=p[j]时,执行下面else代码,该代码表示此时模式串需要向右移动,即为了使下一次while循环比较时候使用第next[j]个字符和主串中的第i个字符匹配比较。
else j=next[j];
}
//当循环结束时候,next数组中存放的就是模式串的next初始化的全部数值。即当当前模式串中的字符和主串中的值不匹配时候,下一步需要使用哪一个字符和主串中的当前字符比较匹配。
}
KMP算法的next函数求解和分析过程的更多相关文章
- KMP算法中next函数的理解
首先要感谢http://blog.csdn.net/v_july_v/article/details/7041827以及http://blog.chinaunix.net/uid-27164517-i ...
- KMP算法番外篇--求解next数组
KMP算法实现字符串的模式匹配的时间复杂度比朴素的模式匹配好很多,但是它时间效率的提高是有前提的,那就是:模式串的重复率很高,不然它的效率也不会凸显出来.在实际的应用中,KMP算法不算是使用率很高的一 ...
- KMP算法的时间复杂度与next数组分析
一.什么是 KMP 算法 KMP 算法是一种改进的字符串匹配算法,用于判断一个字符串是否是另一个字符串的子串 二.KMP 算法的时间复杂度 O(m+n) 三.Next 数组 - KMP 算法的核心 K ...
- <字符串匹配>KMP算法为何比暴力求解的时间复杂度更低?
str表示文本串,m表示模式串; str[i+j] 和 m[j] 是正在进行匹配的字符; KMP的时间复杂度是O(m+n) , 暴力求解的时间复杂度是O(m*n) KMP利用了B[0:j]和A[i ...
- 一个std::sort 自定义比较排序函数 crash的分析过程
两年未写总结博客,今天先来练练手,总结最近遇到的一个crash case. 注意:以下的分析都基于GCC4.4.6 一.解决crash 我们有一个复杂的排序,涉及到很多个因子,使用自定义排序函数的st ...
- [C++] [算法] KMP算法
KMP串匹配算法是一个经典的算法. 传统BF算法是传统的字符串匹配算法.很好理解.叶实现.但时间复杂度太高. 本文将从字符串模式字符串被称为.为了匹配字符串被称为主弦. KMP配时能够少移动从串的位置 ...
- 第4章学习小结_串(BF&KMP算法)、数组(三元组)
这一章学习之后,我想对串这个部分写一下我的总结体会. 串也有顺序和链式两种存储结构,但大多采用顺序存储结构比较方便.字符串定义可以用字符数组比如:char c[10];也可以用C++中定义一个字符串s ...
- 数据结构4_java---顺序串,字符串匹配算法(BF算法,KMP算法)
1.顺序串 实现的操作有: 构造串 判断空串 返回串的长度 返回位序号为i的字符 将串的长度扩充为newCapacity 返回从begin到end-1的子串 在第i个字符之前插入字串str 删除子串 ...
- KMP算法复杂度证明
引言 KMP算法应该是看了一次又一次,比赛的时候字符串不是我负责,所以学到的东西又还给网上的博客了-- 退役后再翻开看,看到模板,心想这不是\(O(n^2)\)的复杂度吗? 有两个循环也不能看做是\( ...
随机推荐
- tinyweb集成springmvc 的一种可行方式
最近tiny项目中集成了springmvc,而且使用的tiny的版本比较低,所以整合起来官网给的前两种方式都行不通. 而且有个tiny整合springmvc的maven依赖都下载不了.所以只有使用第三 ...
- Linux创建一个周期任务来定期删除过期的文件
一:需求 在开发中存在这样的情况,为了防止文件的误删,不允许开发人员直接删除项目中要用到的文件,而是将它们移动到某个目录,然后由一个周期任务去检测并删除内部过期的文件: 二:检测文件是否是过期文件 有 ...
- 一个简单的MySQL多实例环境搭建
安装mysql 初始化两个数据库目录 mysql_install_db --datadir=/usr/local/var/mysql1 --user=mysql mysql_install_db -- ...
- kepware http接口 nodejs开发
读取某变量的值(native var http = require("http"); var options = { "method": "GET&q ...
- 20155326刘美岑 2016-2017-2 《Java程序设计》第5周学习总结
20155326刘美岑 2016-2017-2 <Java程序设计>第5周学习总结 教材学习内容总结 使用 try.catch (1)java中所有的错误都会被打包为对象,如果愿意,可以尝 ...
- noip第20课作业
1. 评学习小标兵 [问题描述] 东东所在的班级有 N 名同学,期末考试进行了数学.语文.英语.地理四门功课的测试.班主任要将这 N 名学生中总分前三名定为本学期的“学习小标兵”.现在给出这N 名学生 ...
- 团队项目之UML图设计---WeEdit
团队信息: 学号: 姓名: 本次博客链接: 041602209 黄毓明(临时队长) https://www.cnblogs.com/mingsonic/p/9820702.html 06160023 ...
- D3_book 11.2 stack
<!-- book :interactive data visualization for the web 11.2 stack 一个堆叠图的例子 --> <!DOCTYPE htm ...
- HDU 3078 LCA转RMQ
题意: n个点 m个询问 下面n个数字表示点权值 n-1行给定一棵树 m个询问 k u v k为0时把u点权值改为v 或者问 u-v的路径上 第k大的数 思路: LCA转RMQ求出 LCA(u,v) ...
- Javascript 对象继承 原型链继承 对象冒充 call 混合方式
一.原型链继承 function ClassA() {} ClassA.prototype.color = "blue"; ClassA.prototype.sayColor = ...