在讲KMP之前请允许我回顾一下BF算法,那么什么叫BF算法呢?就是最普通的暴力,请见下方的代码
1 int return_pos(string S,string P) { 2 int i = 0,j = 0,k = 0; 3 int Slen = S.length(); 4 int Plen = P.length(); 5 while (i < Slen && j < Plen) { 6 if (S[i + k] == P[j]) { 7 ++k,++j; 8 } 9 else { 10 i++,j = 0,k = 0; 11 } 12 } 13 if (j == Plen) return i; 14 return -1; // 返回-1表示不匹配 15 }
通过上面的代码,估计你已经知道了什么叫做BF了吧;(在看不懂上面代码之前不要往下看,看了你也不懂,最好弄懂每个字母代表的含义,这是我没有加注释的原因之一,原因之二呢,实在是不想打注释。。。O(∩_∩)O哈哈~。。。)
好的,接下来就到我们的重点了,KMP算法;
正如上面的BF算法所示,当每一次不匹配的时候,i回到原来位置,然后i++,j= 0;可以看出每一次的i回到原来位置,j 赋值为0,
是不是太麻烦了,如果串的长度太大,你懂得。。。。。
好了,KMP这个时候来了,他们就想啊,如果我们不让i回到原来位置(也就是说不要k(BF中的)了),只让j在动,能不能实现这个字符串匹配呢,
wow,最后他们成功了,(是时候该骂他们了,就是因为他们成功了,我们还要学这破算法,学就学吧,还TMD那么难理解)
废话不说,请看下方。
那么我们来个例子,我们设S串ababc,P串为abc。好的,那么我们先看一下,BF是怎么实现的。。。。。
S a(babc) ab(abc) aba(bc)
P a(bc) ab(c) abc
(我擦。。。不匹配,白忙活了,看我回溯。。。)
S ab(abc)
P a(bc)
(我擦来。。。还是不匹配,我再回溯。。。)
S aba(bc) abab(c) ababc
P a(bc) ab(c) abc
(要西,终于匹配了。)
接下来看KMP是怎么做的
S a(babc) ab(abc) aba(bc)
P a(bc) ab(c) abc
(我擦。。。不匹配,白忙活了,看我next。。。)
S aba(bc) abab(c) ababc
P a(bc) ab(c) abc
(牛逼吧。。。。。)
通过看这组样例,估计大家已经觉得KMP的神奇之处了。(难道你不想问我next是干么的吗?什么?你不问?那我也要跟你说,接着往下看。。。)
那么重点来了。下面解释一下啊next数组的含义,这也是KMP不太好理解的地方。
令原始串为S(是S,不是P)长度为n,模式串为T(是T!是T!!)长度为m;
我们假设目前匹配到如下位置
S0,S1,S2,S3,.....,Si-j,Si-j+1,...........Si-1,Si,Si+1,.......Sn
T0,T1,...............Tj-1,Tj,............
(绿色的部分为匹配部分,红色为不匹配的部分)估计你看出来了吧,恰好Si和Tj的时候,失配,那么我们将不会动i的位置。动用next神器。
1)如果模式串右移1位(为了简单,就让我假设1位吧),即 next[j] == j - 1(对吧),好,那看看接下的KMP是怎么做的
S0,S1,S2,S3,.....,Si-j,Si-j+1,...........Si-1,Si,Si+1,.......Sn
T0,T1,...............Tj-1,Tj,............
T0,T1.................,Tj-1,Tj.............
(蓝色的部分是即将要看看是否匹配的字符) 看到了吧,是不是省下了好多不必要的麻烦。
(难道你没有问题要问我吗?什么?没有?真没有?你确定?那我问你一个问题,请看下方)
S1,S2,S3,.....,Si-j,Si-j+1,...........Si-1,Si,Si+1,.......Sn
T0,T1...........,Tj-2,Tj-1,Tj.............
(好,请回答我,为什么绿色的部分是匹配的呢?什么?我说的,我什么时候说的?看到粉色的字符了吗?原来Si-j与T0匹配,哈哈,回答不出来吧,让我们
回想一下next的作用,为什么next[j] == j - 1,因为T0,T1......Tj - 2 == T1,T2........Tj - 1。。。。看到这个是不是你想到什么东西,对,就是老师上课讲的那个令人头痛的公式,没记错的的话,应该在PPT28页,你可以回去看看,现在应该可以看的懂了。)
通过这个例子,我现在就可以告诉你了,next[j] 就是记录T[0...j-1]中前缀和后缀相等的最大长度。
那么接下来,我就敲一遍KMP的核心算法。请看下方。。。
1 void Get_next(string P,int* next) { 2 int i,j; 3 next[i = 0] = j = -1; 4 int len = P.length(); 5 while (i < len - 1) { 6 if (j == -1 || P[i] == P[j]) { 7 next[++i] = ++j; 8 } 9 else j = next[j]; 10 } 11 }
代码给你了,估计你能看得懂吧。
那么我就做个总结吧。。。归根究底呢,KMP算法的本质便是:针对匹配的模式串的特点,判断它是否有重复的字符,从而找到它的前缀与后缀,进而求出相应的Next数组,最终根据Next数组而进行KMP匹配。接下来,进入第二部分nextval数组。
请见连接(http://www.cnblogs.com/xiaoshanshan/p/4082011.html );