当前位置:文档之家› KMP算法思想

KMP算法思想

KMP算法思想
KMP算法思想

KMP字符串模式匹配详解

来自CSDN A_B_C_ABC网友

KMP字符串模式匹配通俗点说就是一种在一个字符串中定位另一个串的高效算法。简单匹配算法的时间复杂度为O(m*n);KMP匹配算法。可以证明它的时间复杂度为O(m+n).。

一.简单匹配算法

先来看一个简单匹配算法的函数:

int Index_BF ( char S [ ], char T [ ], int pos )

{

/*若串S中从第pos(S的下标0≤pos

这样的子串在串S中的下标,否则返回-1 */

int i = pos, j = 0;

while ( S[i+j] != '\0'&& T[j] != '\0')

if ( S[i+j] == T[j] )

j ++;//继续比较后一字符

else

{

i ++; j = 0;//重新开始新的一轮匹配

}

if ( T[j] == '\0')

return i;//匹配成功返回下标

else

return -1;//串S中(第pos个字符起)不存在和串T相同的子串

}// Index_BF

此算法的思想是直截了当的:将主串S中某个位置i起始的子串和模式串T 相比较。即从j=0起比较S[i+j]与T[j],若相等,则在主串S中存在以i为起始位置匹配成功的可能性,继续往后比较( j逐步增1 ),直至与T串中最后一个字符相等为止,否则改从S串的下一个字符起重新开始进行下一轮的"匹配",即将串T向后滑动一位,即i增1,而j退回至0,重新开始新一轮的匹配。

例如:在串S=”abcabcabdabba”中查找T=” abcabd”(我们

可以假设从下标0开始):先是比较S[0]和T[0]是否相等,然后比较S[1]和T[1]是否相等…我们发现一直比较到S[5]和T[5]才不等。如图:

当这样一个失配发生时,T下标必须回溯到开始,S下标回溯的长度与T 相同,然后S下标增1,然后再次比较。如图:

这次立刻发生了失配,T下标又回溯到开始,S下标增1,然后再次比较。如图:

这次立刻发生了失配,T下标又回溯到开始,S下标增1,然后再次比较。如图:

又一次发生了失配,所以T下标又回溯到开始,S下标增1,然后再次比较。这次T中的所有字符都和S中相应的字符匹配了。函数返回T在S中的起始下标3。如图:

二. KMP匹配算法

还是相同的例子,在S=”abcabcabdabba”中查找T=”abcabd”,如果使

用KMP匹配算法,当第一次搜索到S[5]和T[5]不等后,S下标不是回溯到1,T 下标也不是回溯到开始,而是根据T中T[5]==’d’的模式函数值(next[5]=2,为什么?后面讲),直接比较S[5]和T[2]是否相等,因为相等,S和T的下标同时增加;因为又相等,S和T的下标又同时增加。。。最终在S中找到了T。如图:

KMP匹配算法和简单匹配算法效率比较,一个极端的例子是:

在S=“AAAAAA…AAB“(100个A)中查找T=”AAAAAAAAAB”,简单匹配算法每次都是比较到T的结尾,发现字符不同,然后T的下标回溯到开始,S 的下标也要回溯相同长度后增1,继续比较。如果使用KMP匹配算法,就不必回溯.

对于一般文稿中串的匹配,简单匹配算法的时间复杂度可降为O (m+n),因此在多数的实际应用场合下被应用。

KMP算法的核心思想是利用已经得到的部分匹配信息来进行后面的匹配过程。看前面的例子。为什么T[5]==’d’的模式函数值等于2(next[5]=2),其实这个2表示T[5]==’d’的前面有2个字符和开始的两个字符相同,且T[5]==’d’不等于开始的两个字符之后的第三个字符(T[2]=’c’).如图:

也就是说,如果开始的两个字符之后的第三个字符也为’d’,那么,尽管T[5]==’d’的前面有2个字符和开始的两个字符相同,T[5]==’d’的模式函数值也不为2,而是为0。

前面我说:在S=”abcabcabdabba”中查找T=”abcabd”,如果使用KMP匹配算法,当第一次搜索到S[5]和T[5]不等后,S下标不是回溯到1,T下标也不是回溯到开始,而是根据T中T[5]==’d’的模式函数值,直接比较S[5]和T[2]是否相等。。。为什么可以这样?

刚才我又说:“(next[5]=2),其实这个2表示T[5]==’d’的前面有2个字符和开始的两个字符相同”。请看图:因为,S[4] ==T[4],S[3] ==T[3],根据next[5]=2,有T[3]==T[0],T[4] ==T[1],所以S[3]==T[0],S[4] ==T[1](两对相当于间接比较过了),因此,接下来比较S[5]和T[2]是否相等。。。

有人可能会问:S[3]和T[0],S[4]和T[1]是根据next[5]=2间接比较相等,那S[1]和T[0],S[2]和T[0]之间又是怎么跳过,可以不比较呢?因为S[0]=T[0],S[1]=T[1],S[2]=T[2],而T[0] != T[1], T[1] != T[2],==> S[0] != S[1],S[1] != S[2],所以S[1] != T[0],S[2] != T[0]. 还是从理论上间接比较了。

有人疑问又来了,你分析的是不是特殊轻况啊。

假设S不变,在S中搜索T=“abaabd”呢?答:这种情况,当比较到S[2]和T[2]时,发现不等,就去看next[2]的值,next[2]=-1,意思是S[2]已经和T[0]间接比较过了,不相等,接下来去比较S[3]和T[0]吧。

假设S不变,在S中搜索T=“abbabd”呢?答:这种情况当比较到S[2]

和T[2]时,发现不等,就去看next[2]的值,next[2]=0,意思是S[2]已经和T[2]比较过了,不相等,接下来去比较S[2]和T[0]吧。

假设S=”abaabcabdabba”在S中搜索T=“abaabd”呢?答:这种情况当

比较到S[5]和T[5]时,发现不等,就去看next[5]的值,next[5]=2,意思是前面的比较过了,其中,S[5]的前面有两个字符和T的开始两个相等,接下来去比较

S[5]和T[2]吧。

总之,有了串的next值,一切搞定。那么,怎么求串的模式函数值next[n]呢?(本文中next值、模式函数值、模式值是一个意思。)

三.怎么求串的模式值next[n]

定义:

(1)next[0]= -1 意义:任何串的第一个字符的模式值规定为-1。

(2)next[j]= -1 意义:模式串T中下标为j的字符,如果与首字符

相同,且j的前面的1—k个字符与开头的1—k

个字符不等(或者相等但T[k]==T[j])(1≤k

如:T=”abCabCad”则next[6]=-1,因T[3]=T[6]

(3)next[j]=k 意义:模式串T中下标为j的字符,如果j的前面k个

字符与开头的k个字符相等,且T[j] != T[k](1≤k

即T[0]T[1]T[2]。。。T[k-1]==

T[j-k]T[j-k+1]T[j-k+2]…T[j-1]

且T[j] != T[k].(1≤k

(4) next[j]=0 意义:除(1)(2)(3)的其他情况。

举例:

01)求T=“abcac”的模式函数的值。

next[0]= -1 根据(1)

next[1]=0 根据(4) 因(3)有1<=k

next[2]=0 根据(4) 因(3)有1<=k

next[3]= -1 根据(2)

next[4]=1 根据(3) T[0]=T[3]且T[1]=T[4]

下标01234

T a b c a c

next-100-11若T=“abcab”将是这样:

下标01234

T a b c a b

next-100-10

为什么T[0]==T[3],还会有next[4]=0呢,因为T[1]==T[4],根据(3)”且

T[j] != T[k]”被划入(4)。

02)来个复杂点的,求T=”ababcaabc”的模式函数的值。

next[0]= -1 根据(1)

next[1]=0 根据(4)

next[2]=-1 根据(2)

next[3]=0 根据(3)虽T[0]=T[2]但T[1]=T[3]被划入(4)

next[4]=2 根据(3) T[0]T[1]=T[2]T[3]且T[2] !=T[4]

next[5]=-1 根据(2)

next[6]=1 根据(3) T[0]=T[5]且T[1]!=T[6]

next[7]=0 根据(3)虽T[0]=T[6]但T[1]=T[7]被划入(4)

next[8]=2 根据(3) T[0]T[1]=T[6]T[7]且T[2] !=T[8]

下标012345678

T a b a b c a a b c

next-10-102-1102

只要理解了next[3]=0,而不是=1,next[6]=1,而不是= -1,next[8]=2,

而不是= 0,其他的好象都容易理解。

03)来个特殊的,求T=”abCabCad”的模式函数的值。

下标01234567

T a b C a b C a d

next-100-100-14

next[5]= 0 根据(3)虽T[0]T[1]=T[3]T[4],但T[2]==T[5]

next[6]= -1 根据(2)虽前面有abC=abC,但T[3]==T[6]

next[7]=4 根据(3)前面有abCa=abCa,且T[4]!=T[7]

若T[4]==T[7],即T=” adCadCad”,那么将是这样:next[7]=0,而不是= 4,

因为T[4]==T[7].

下标01234567

T a d C a d C a d

next-100-100-10

如果你觉得有点懂了,那么

练习:求T=”AAAAAAAAAAB”的模式函数值,并用后面的求模式函数值函数

验证。

意义:

next函数值究竟是什么含义,前面说过一些,这里总结。

设在字符串S中查找模式串T,若S[m]!=T[n],那么,取T[n]的模式函数值next[n],

1.next[n]= -1表示S[m]和T[0]间接比较过了,不相

等,下一次比较S[m+1]和T[0]

2.next[n]=0表示比较过程中产生了不相等,下一

次比较S[m]和T[0]。

3.next[n]= k >0但k

中的开始k个字符已经间接比较相等了,下一次

比较S[m]和T[k]相等吗?

4.其他值,不可能。

四.求串T的模式值next[n]的函数

说了这么多,是不是觉得求串T的模式值next[n]很复杂呢?要叫我写个函

数出来,目前来说,我宁愿去登天。好在有现成的函数,当初发明KMP算法,写出这个函数的先辈,令我佩服得六体投地。我等后生小子,理解起来,都要反复琢磨。下面是这个函数:

void get_nextval(const char *T, int next[])

{

//求模式串T的next函数值并存入数组next。

int j = 0, k = -1;

next[0] = -1;

while ( T[j/*+1*/] != '\0' )

{

if (k == -1 || T[j] == T[k])

{

++j; ++k;

if (T[j]!=T[k])

next[j] = k;

else

next[j] = next[k];

}// if

else

k = next[k];

}// while

////这里是我加的显示部分

// for(int i=0;i

//{

// cout<

//}

//cout<

}// get_nextval

另一种写法,也差不多。

void getNext(const char* pattern,int next[])

{

next[0]= -1;

int k=-1,j=0;

while(pattern[j] != '\0')

{

if(k!= -1 && pattern[k]!= pattern[j] )

k=next[k];

++j;++k;

if(pattern[k]== pattern[j])

next[j]=next[k];

else

next[j]=k;

}

////这里是我加的显示部分

// for(int i=0;i

//{

// cout<

//}

//cout<

}

下面是KMP模式匹配程序,各位可以用他验证。记得加入上面的函数

#include

#include

int KMP(const char *Text,const char* Pattern) //const表示函数内部不会改变这个参数的值。

{

if( !Text||!Pattern|| Pattern[0]=='\0' || Text[0]=='\0' )//

return -1;//空指针或空串,返回-1。

int len=0;

const char * c=Pattern;

while(*c++!='\0')//移动指针比移动下标快。

{

++len;//字符串长度。

}

int *next=new int[len+1];

get_nextval(Pattern,next);//求Pattern的next函数值

int index=0,i=0,j=0;

while(Text[i]!='\0' && Pattern[j]!='\0' )

{

if(Text[i]== Pattern[j])

{

++i;//继续比较后继字符

++j;

}

else

{

index += j-next[j];

if(next[j]!=-1)

j=next[j];//模式串向右移动

else

{

j=0;

++i;

}

}

}//while

delete []next;

if(Pattern[j]=='\0')

return index;//匹配成功

else

return -1;

}

int main()//abCabCad

{

char* text="bababCabCadcaabcaababcbaaaabaaacababcaabc";

char*pattern="adCadCad";

//getNext(pattern,n);

//get_nextval(pattern,n);

cout<

return 0;

}

五.其他表示模式值的方法

上面那种串的模式值表示方法是最优秀的表示方法,从串的模式值我们可以得到很多信息,以下称为第一种表示方法。第二种表示方法,虽然也定义next[0]= -1,但后面绝不会出现-1,除了next[0],其他模式值next[j]=k(0≤k

简单看成是:下标为j的字符的前面最多k个字符与开始的k个字符相同,这里并不要求T[j] != T[k]。其实next[0]也可以定义为0(后面给出的求串的模式值的函数和串的模式匹配的函数,是next[0]=0的),这样,next[j]=k(0≤k

没看到详细解释,我估计是为那些这样的编程语言准备的:数组的下标从1开始

而不是0。

下面给出几种方法的例子:

表一。

下标012345678

T a b a b c a a b c

(1) next-10-102-1102

(2) next-100120112

(3) next010130213

第三种表示方法,在我看来,意义不是那么明了,不再讨论。

表二。

下标01234

T a b c a c

(1)next-100-11

(2)next-10001

表三。

下标01234567

T a d C a d C a d

(1)next-100-100-10

(2)next-10001234

对比串的模式值第一种表示方法和第二种表示方法,看表一:

第一种表示方法next[2]= -1,表示T[2]=T[0],且T[2-1] !=T[0]

第二种表示方法next[2]= 0,表示T[2-1] !=T[0],但并不管T[0]和T[2]相不相等。

第一种表示方法next[3]= 0,表示虽然T[2]=T[0],但T[1] ==T[3]

第二种表示方法next[3]= 1,表示T[2] =T[0],他并不管T[1]和T[3]相不相等。

第一种表示方法next[5]= -1,表示T[5]=T[0],且T[4] !=T[0],

T[3]T[4] !=T[0]T[1],T[2]T[3]T[4] !=T[0]T[1]T[2]

第二种表示方法next[5]= 0,表示T[4] !=T[0],T[3]T[4] !=T[0]T[1],

T[2]T[3]T[4] !=T[0]T[1]T[2],但并不管T[0]和T[5]相不相等。换句话说:就算

T[5]==’x’,或T[5]==’y’,T[5]==’9’,也有next[5]= 0。

从这里我们可以看到:串的模式值第一种表示方法能表示更多的信息,第二

种表示方法更单纯,不容易搞错。当然,用第一种表示方法写出的模式匹配函数

效率更高。比如说,在串S=“adCadCBdadCadCad 9876543”中匹配串T=“adCadCad”,用第一种表示方法写出的模式匹配函数,当比较到S[6] != T[6]时,

取next[6]= -1(表三),它可以表示这样许多信息:

S[3]S[4]S[5]==T[3]T[4]T[5]==T[0]T[1]T[2],而S[6] != T[6],T[6]==T[3]==T[0],

所以S[6] != T[0],接下来比较S[7]和T[0]吧。如果用第二种表示方法写出的模式

匹配函数,当比较到S[6] != T[6]时,取next[6]= 3(表三),它只能表示:

S[3]S[4]S[5]== T[3]T[4]T[5]==T[0]T[1]T[2],但不能确定T[6]与T[3]相不相等,

所以,接下来比较S[6]和T[3];又不相等,取next[3]= 0,它表示S[3]S[4]S[5]==

T[0]T[1]T[2],但不会确定T[3]与T[0]相不相等,即S[6]和T[0]相不相等,所以

接下来比较S[6]和T[0],确定它们不相等,然后才会比较S[7]和T[0]。是不是比

用第一种表示方法写出的模式匹配函数多绕了几个弯。

为什么,在讲明第一种表示方法后,还要讲没有第一种表示方法好的第二种

表示方法?原因是:最开始,我看严蔚敏的一个讲座,她给出的模式值表示方法

是我这里的第二种表示方法,如图:

她说:“next函数值的含义是:当出现S[i] !=T[j]时,下一次的比较应该在S[i]

和T[next[j]] 之间进行。”虽简洁,但不明了,反复几遍也没明白为什么。而她

给出的算法求出的模式值是我这里说的第一种表示方法next值,就是前面的

get_nextval()函数。匹配算法也是有瑕疵的。于是我在这里发帖说她错了:

https://www.doczj.com/doc/0112620940.html,/Expert/topic/4413/4413398.xml?temp=.2027246现在看来,她没有错,不过有张冠李戴之嫌。我不知道,是否有人第一次学

到这里,不参考其他资料和明白人讲解的情况下,就能搞懂这个算法(我的意思是不仅是算法的大致思想,而是为什么定义和例子中next[j]=k(0≤k

书归正传,下面给出我写的求第二种表示方法表示的模式值的函数,为了从S 的任何位置开始匹配T,“当出现S[i] !=T[j]时,下一次的比较应该在S[i]和

T[next[j]] 之间进行。”定义next[0]=0。

v oid myget_nextval(const char *T, int next[])

{

//求模式串T的next函数值(第二种表示方法)并存入数组next。

int j = 1, k = 0;

next[0] = 0;

while ( T[j] != '\0' )

{

if(T[j] == T[k])

{

next[j] = k;

++j; ++k;

}

else if(T[j] != T[0])

{

next[j] = k;

++j;

k=0;

}

else

{

next[j] = k;

++j;

k=1;

}

}//while

for(int i=0;i

{

cout<

}

cout<

}// myget_nextval

下面是模式值使用第二种表示方法的匹配函数(next[0]=0)

int my_KMP(char *S, char *T, int pos)

{

int i = pos, j = 0;//pos(S的下标0≤pos

while ( S[i] != '\0' && T[j] != '\0' )

{

if (S[i] == T[j] )

{

++i;

++j; //继续比较后继字符

}

else // a b a b c a a b c

// 0 0 0 1 2 0 1 1 2

{ //-1 0 -1 0 2 -1 1 0 2

i++;

j = next[j]; /*当出现S[i] !=T[j]时,

下一次的比较应该在S[i]和T[next[j]] 之间进行。要求next[0]=0。

在这两个简单示范函数间使用全局数组next[]传值。*/

}

}//while

if ( T[j] == '\0' )

return (i-j); //匹配成功

else

return -1;

} // my_KMP

完全掌握KMP算法思想

学过数据结构的人,都对KMP算法印象颇深。尤其是新手,更是难以理解其涵义,搞得一头雾水。今天我们就来面对它,不将它彻底搞懂,誓不罢休。

如今,大伙基本上都用严蔚敏老师的书,那我就以此来讲解KMP算法。(小弟正在备战考研,为了节省时间,很多课本上的话我都在此省略了,以后一定补上。)

严老的《数据结构》79页讲了基本的匹配方法,这是基础。先把这个搞懂了。

80页在讲KMP算法的开始先举了个例子,让我们对KMP的基本思想有了最初的认识。目的在于指出“由此,在整个匹配的过程中,i指针没有回溯,”。

我们继续往下看:

现在讨论一般情况。

假设主串:s: ‘s(1) s(2) s(3) ……s(n)’; 模式串:p: ‘p(1) p(2) p(3)…..p(m)’

把课本上的这一段看完后,继续

现在我们假设主串第i个字符与模式串的第j(j<=m)个字符‘失配’后,主串第i个字符与模式串的第k(k

此时,s(i)≠p(j),有

主串:S(1)…… s(i-j+1)…… s(i-1) s(i) ………….

|| (相配) || ≠(失配)

匹配串:P(1) ……. p(j-1) p(j)

由此,我们得到关系式

‘p(1) p(2) p(3)…..p(j-1)’ = ’ s(i-j+1)……s(i-1)’

由于s(i)≠p(j),接下来s(i)将与p(k)继续比较,则模式串中的前(k-1)个字符的子串必须满足下列关系式,并且不可能存在k’>k满足下列关系式:(k

‘p(1) p(2) p(3)…..p(k-1)’ = ’ s(i-k+1)s(i-k+2)……s(i-1)’

即:

主串:S(1)……s(i-k +1) s(i-k +2) ……s(i-1) s(i) ………….

|| (相配) || || ?(有待比较)

匹配串: P(1) p(2) …… p(k-1) p(k)

现在我们把前面总结的关系综合一下

有:

S(1)…s(i-j +1)… s(i-k +1) s(i-k +2) …… s(i-1) s(i) ……

|| (相配) || || || ≠(失配)

P(1) ……p(j-k+1) p(j-k+2) ….... p(j-1) p(j)

|| (相配) || || ?(有待比较)

P(1) p(2) ……. p(k-1) p(k)

由上,我们得到关系:

‘p(1) p(2) p(3)…..p(k-1)’ = ’ s(j-k+1)s(j-k+2)……s(j-1)’

接下来看“反之,若模式串中存在满足式(4-4)。。。。。。。”这一段。看完这一段,如果下面的看不懂就不要看了。直接去看那个next函数的源程序。(伪代码)

K 是和next有关系的,不过在最初看的时候,你不要太追究k到底是多少,至于next值是怎么求出来的,我教你怎么学会。

课本83页不是有个例子吗?就是图4.6

你照着源程序,看着那个例子慢慢的推出它来。看看你做的是不是和课本上正确的next值一样。然后找几道练习题好好练练,一定要做熟练了。现在你的脑子里已经有那个next算法的初步思想了,再回去看它是怎么推出来的,如果还看不懂,就继续做练习,做完练习再看。相信自己!!!

附:

KMP算法查找串S中含串P的个数count

#include

#include

#include

using namespace std;

inline void NEXT(const string& T,vector& next)

{

//按模式串生成vector,next(T.size())

next[0]=-1;

for(int i=1;i

int j=next[i-1];

while(T[i]!=T[j+1]&& j>=0 )

j=next[j] ; //递推计算

if(T[i]==T[j+1])next[i]=j+1;

else next[i]=0; //

}

}

inline string::size_type COUNT_KMP(const string& S,

const string& T)

{

//利用模式串T的next函数求T在主串S中的个数count的KMP算法

//其中T非空,

vector next(T.size());

NEXT(T,next);

string::size_type index,count=0;

for(index=0;index

int pos=0;

string::size_type iter=index;

while(pos

if(S[iter]==T[pos]){

++iter;++pos;

}

else{

if(pos==0)++iter;

else pos=next[pos-1]+1;

}

}//while end

if(pos==T.size()&&(iter-index)==T.size())++count;

} //for end

return count;

}

int main(int argc, char *argv[])

{

string S="abaabcacabaabcacabaabcacabaabcacabaabcac";

string T="ab";

string::size_type count=COUNT_KMP(S,T);

cout<

system("PAUSE");

return 0;

}

关于KMP算法当中的next函数

首先先贴出KMP算法的框架代码,这段代码使用C语言当中的字符串数据结构,因此字符串当中第一个字符的下标为零。

int Index(const char * str1,const char * str2,int pos)

{

int * nextFunc = get_next(str2);

int strLen = strlen(str1);

int subLen = strlen(str2);

int i = pos,j = 0;

while (i < strLen && j < subLen)

{

if (j == -1 || str1[i] == str2[j])

{

i++;

j++;

}

else

j = nextFunc[j];

}

if (j == subLen)

return i - subLen;

return -1;

}

相比较那种最简单的算法而言这里的神奇之处在于一个next函数,由于这个next 函数的存在导致我们在模式匹配过程当中某个字符出现失配的情况时不再需要回溯主串当中的指针i到开始匹配时的位置。所有的数据结构或者算法的书都告诉我们说,之所以不需要回溯这个i指针是因为在匹配过程当中产生了一些附加的信息,利用这些附加信息就可以得到这样的性能改进。

首先我们必须搞清楚这个神奇的next函数的含义。next[j]=k 这样一个式子表示的含义是,当主串当中第i个元素与模式串当中第j个元素不匹配时我们应该保持i指针不动而将模式串当中的j指针移动到k这个位置,然后再比较主串的第i个元素与模式串的第k 个元素是否匹配,匹配当然没话说照最传统的算法移动两个指针比较下一个元素或者得到完全匹配的结果,不匹配那么再做那个动作,也就是求next[k]=?,然后再比较。

之所以存在这么一个next函数是因为,如果说主串与模式串在匹配过程当中主串的第i个元素与模式串的第j个元素不同,那么隐含的意义是主串的从第i-j+1个元素到第i-1个元素与模式串的第1个元素到第j-1个元素是相同的。那么如果说这样不能达到最后全部匹配的结果也就是上面讲的主串[i] != 模式串[j],那么我们应该从主串的i-j+1到i-1这个字串当中从后到前寻找一个最大子串与模式串的第1到j-1这个字串的从第一个到某个元素的最大子串完全匹配。而我们又知道主串中第i-j+1个元素到第i-1个元素的子串事实上就是模式串当中第1个元素到第j-1个元素所形成的子串。next函数所完成的工作就是这个寻找匹配的工作,他的返回值就是这个子串的最后一个元素的下一个位置。为什么是这个位置,前面讲的很清楚,就是说既然前面那一串匹配,那么接下来要比较的就是这个位置的元素。下面开始描述next函数的求法。

从上面的描述我们可以知道next函数的值完全只与模式串相关而与主串是什么样子的没有任何关系,因此来说对于每个模式串都有一个唯一的next串值。求法是这样,如果说自变量为0也就是说第一个元素的next值固定为-1(-1的意思是说,主串的当前位置元素与模式串第一个元素的前一个元素匹配,隐含意义是讲主串指针必须往后移一位再与模式串的第一个元素比较);如果next[j] = k也就是说模式串的第1个元素到第k个元素与第j-k+1个元素到第j-1个元素相等相等(可以按照上面的方法推出到主串上哪几个元素),而且有模式串[j] == 模式串[k]那么可以得到next[j+1] = k+1(这里的原理显而易见);如果不等那么就求另外一个最大子串,方法就是j = next[k],然后再回到上面的比较。而其他的情况就视作

next值为0(事实上只有求j=1时的next值才会出现,所以next值的前两个元素固定是0和1)。具体算法如下:

int * get_next(const char * str)

{

int strLen = strlen(str);

int * nextFunc = new int[strLen];

if (!nextFunc)

return 0;

nextFunc[0] = -1;

int i = 0,j = nextFunc[i];

while (i < strLen)

{

if (j == -1 || str[i] == str[j])

{

i++;

j++;

nextFunc[i] = next[i-1] + 1;

}

else

j = nextFunc[j];

}

return nextFunc;

}

注:本文所有的串表示方法都是C语言的默认表示方法,也就是第一个元素的下标为0

本文来自CSDN博客,转载请标明出处:https://www.doczj.com/doc/0112620940.html,/phil2036/archive/2008/01/27/2068674.aspx

2 KMP算法:

KMP算法是由D.E.Knuth(克努特),J.H.Morris(莫里斯),V.R.Pratt(普拉特)等人共同提出的,该算法主要消除了主串指针(i指针)的回溯,利用已经得到的部分匹配结果将模式串右滑尽可能远的一段距离再继续比较,从而使算法效率有某种程度的提高,O(n+m)。

先从例子入手(p82):

按Brute-Force算法i=i-j+2=2-2+2=2,j=1

按Brute-Force算法i=i-j+2=2-1+2=3,j=1

按Brute-Force算法i=i-j+2=8-6+2=4,j=1,但从已匹配的情况看,模式串在t[6]即“c”前的字符都是匹配的,再看已匹配的串“abaab”,t[1]t[2]与t[4]t[5]相同,那么,因为t[4]t[5]与原串s[6]s[7]匹配,所以t[1]t[2]必然与原串s[6]s[7]匹配,因此说t[3]可以直接与s[8]匹配,按KMP 算法i=8,j=3

匹配成功。从上例看出在匹配不成功时,主串指针i不动,j指针也不回到第一个位置,而是回到一个恰当的位置,如果这时让j指针回到第一个位置,就可能错过有效的匹配,所以在主串指针i不动的前提下,j指针回到哪个位置是问题的关键,既不能将j右移太大,而错过有效的匹配,另一方面,又要利用成功的匹配,将j右移尽可能地大,而提高匹配的效率,因此问题的关键是寻找模式串自身的规律。

//

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////

//

和直接比较若不满足和直接比较所以:满足:

,设1i i 12112111112121s ),2(;

s )1(""")"2("

"")"1("

"""t t j k t t t t t t t t s s t t t t s s s s k j k j k j k j i j i m n <<====-+-+----+-

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

设s=” s 1 s 2 ... s n ”, t=” t 1 t 2 ... t m ”,在匹配过程中,当s i ≠ t j (1≤i ≤n-m+1,1≤j ≤m)时,存在(前面的j-1个字符已匹配):

” s i-j+1 ... s i-1 ” =” t 1 t 2 ... t j-1 ” (1) 若模式中存在可互相重叠的最长的真子串,满足: ” t 1 t 2 ... t k-1 ”=”t j-k+1 t j-k+2 ... t j-1 ” (2) 其中真子串最短可以是t 1 ,即 t 1。。。t 2-1 (k>1),最长的是t 1。。。t j-1-1 (k

例(p82):主串 a c a b a a b a a b c a c a a b c 模式 a b a a b c a c

现在的问题是要求next 函数,next(j)的作用是,当s i 与t j 匹配失败时,s i 与t next(j)匹配。 next 函数的定义如下:

↓↓↓↓↓↓↓↓↓图形拖放

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

匹配。

与匹配失败时,和当其它情况当此集合不空

函数:

的模式串)(1111s 1}'''')1(|max{10

)(t j next i j i j k j k t t s t t t t j k k j j next next ???

??=∧<<==-+--

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑

a

c b a a b a next c b a a b a next b a a b a next a a b a next a b a next b a next a next next j next abaabcac 2

)8(1)7(3)6(2)5(2)4(1)3(1)2(0)1()(""========的例:求

模式匹配的KMP算法详解

模式匹配的KMP算法详解 模式匹配的KMP算法详解 这种由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现的改进的模式匹配算法简称为KMP算法。大概学过信息学的都知道,是个比较难理解的算法,今天特把它搞个彻彻底底明明白白。 注意到这是一个改进的算法,所以有必要把原来的模式匹配算法拿出来,其实理解的关键就在这里,一般的匹配算法: int Index(String S,String T,int pos)//参考《数据结构》中的程序 { i=pos;j=1;//这里的串的第1个元素下标是1 while(i<=S.Length && j<=T.Length) { if(S[i]==T[j]){++i;++j;} else{i=i-j+2;j=1;}//**************(1) } if(j>T.Length) return i-T.Length;//匹配成功 else return 0; } 匹配的过程非常清晰,关键是当‘失配’的时候程序是如何处理的?回溯,没错,注意到(1)句,为什么要回溯,看下面的例子: S:aaaaabababcaaa T:ababc aaaaabababcaaa ababc.(.表示前一个已经失配) 回溯的结果就是 aaaaabababcaaa a.(babc) 如果不回溯就是 aaaaabababcaaa aba.bc 这样就漏了一个可能匹配成功的情况 aaaaabababcaaa ababc 为什么会发生这样的情况?这是由T串本身的性质决定的,是因为T串本身有前后'部分匹配'的性质。如果T为abcdef这样的,大没有回溯的必要。

KMP算法文献综述

文献综述 一般使用的计算机的硬件结构主要反映数值计算的需要,而计算机上的非数值处理的对象基本上是字符串数据,因此在处理字符串数据时比处理整数和浮点数要复杂的很多。随着程序语言将程序的发展,字符串的处理也有了越来越多的研究。子串的定位炒作通常称为串的模式匹配,是各种处理系统中最重要的操作之一。串匹配问题是指从给定的字符序列中找出一个或多个具有某种属性的模式序列,而字符串匹配指的便是从给定的字符序列中找出一个或若干个给定的字符串。字符串匹配算法是一个基础算法,它的解决以及在这个过程中产生的方法对计算机的其他问题都产生了巨大的影响。在我们日常使用计算机的过程中,使用字符串匹配技术的例子十分普遍,例如:入侵检测、病毒检测、信息检索、信息过滤、计算生物学等等都包含了字符串匹配技术。 在字符串匹配技术被广泛应用的同时,众多的科技人员也对其进行了深入的研究,字符串匹配问题现在已经发展成为一门相对独立的科学——字符串学(Stingology)[1][2][3]。字符串匹配技术最先被应用于图书文献目录摘要的查询系统和构建数据的全文检索系统。而后,随着网络安全技术和生物技术的日益发展,在网络安全和生物计算等领域中字符串匹配技术又获得了新的发展空间。 随着网络速度和流量的日益增加,基于网络的入侵检测[4][5]系统面临着严峻的挑战,它的处理、分析速度越来越难以跟上网络流量增加速度,从而极易导致数据包的丢失。解决数据包丢失等问题,提高处理速度是关键。另外对于基于误用的入侵检测系统而言,检测过程中最费时的部分便是入侵特征匹配。 目前,信息资源的高速膨胀已经成为一个全球普遍关注的现象。加利福尼亚大学伯克利分校研究人员发现,仅从1999年至2002年全球新产生的信息量就翻了一番。伴随着信息膨胀,信息的良莠不齐现象也是一个严重困扰人们的问题。大量反动、黄色信息以及国家机密在网络上蔓延和传播,给国建安全和社会稳定造成了严重的威胁,如何对这些不良信息进行网络监控是我们面临的一个重要问题。在信息过滤时,特别是在主干网络上进行过滤与检索,对字符串匹配的实时性要求极高,字符串匹配性能的优劣直接影响了过滤与检索系统的性能。 随着生命科学的发展,人们对生命物质的微观结构也有了越来越清晰的认识。目前,人类基因组序列的绘制工作已完成,Prosite等大型蛋白质重要样本数

模式匹配KMP算法实验报告

实验四:KMP算法实验报告 一、问题描述 模式匹配两个串。 二、设计思想 这种由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现的改进的模式匹配算法简称为KM P算法。 注意到这是一个改进的算法,所以有必要把原来的模式匹配算法拿出来,其实理解的关键就在这里,一般的匹配算法: int Index(String S,String T,int pos)//参考《数据结构》中的程序 { i=pos;j=1;//这里的串的第1个元素下标是1 while(i<=S.Length && j<=T.Length) { if(S[i]==T[j]){++i;++j;} else{i=i-j+2;j=1;}//**************(1) } if(j>T.Length) return i-T.Length;//匹配成功 else return 0; } 匹配的过程非常清晰,关键是当‘失配’的时候程序是如何处理的?为什么要回溯,看下面的例子: S:aaaaabababcaaa T:ababc aaaaabababcaaa ababc.(.表示前一个已经失配) 回溯的结果就是 aaaaabababcaaa a.(babc) 如果不回溯就是 aaaaabababcaaa aba.bc 这样就漏了一个可能匹配成功的情况 aaaaabababcaaa ababc 这是由T串本身的性质决定的,是因为T串本身有前后'部分匹配'的性质。如果T为a bcdef这样的,大没有回溯的必要。 改进的地方也就是这里,我们从T串本身出发,事先就找准了T自身前后部分匹配的位置,那就可以改进算法。 如果不用回溯,那T串下一个位置从哪里开始呢? 还是上面那个例子,T为ababc,如果c失配,那就可以往前移到aba最后一个a的位置,像这样:

kmp算法实验报告

数据结构 实 验 报 告 学院软件学院 年级2009级 班级班 学号 姓名 2010 年 3 月24 日

目录 一、实验内容 (1) 二、实验过程……………………………………….X 三、实验结果……………………………………….X

一、实验内容: 1、实验题目:KMP算法 2、实验要求:实现教材中字串比较kmp算法,比较模式串abaabcac与主串acabaabaabcacaabc。 3、实验目标:了解并掌握串的类型定义和基本操作,并在此基础上实现kmp算法。了解kmp算法的基本原理和next函数的使用。

二、实验过程: 1、任务分配 2、设计思想 (1)KMP算法:在模式匹配中,每当一趟匹配过程出现字符比较不等时,不需要回溯i指针,而是利用已经得到的“部分匹配”的结果将模式向右滑动尽可能远的一段距离之后,继续进行比较。 (2)next函数:看成一个模式匹配问题,整个模式串既是主串又是模式串,可仿照KMP算法。 3、需求分析 (1) 输入的形式和输入值的范围:输入主串S,模式串T,位置pos (2) 输出的形式:模式串在主串中开始匹配的位置i (3) 程序所能达到的功能:利用kmp算法完成模式串和主串的模式匹 配,并输出模式串在主串中开始匹配的位置 (4) 测试数据: S=acabaabaabcacaabc T=abaabcac Pos=6 4、概要设计 1).抽象数据类型 Class String()定义字符串 Int StrLength()返回串的长度 V oid get_next()求模式串T的next函数值并存入next int kmp()利用模式串T的next函数求出T在主串S中第pos个字符之后的位置的KMP算法 2).算法 a.kmp算法模块:实现主串和模式串的模式匹配 b.next函数模块:实现模式串自身的模式匹配,并存入nxet函数中 c.接收处理命令(初始化数据) 5、详细设计 程序代码(含注释)

KMP算法-如何理解

对KMP算法的理解 整理者——戴红伟 字符匹配算法的现实意义:随着互联网的日渐庞大,信息也是越来越多,如何在海量的信息中快速查找自己所要的信息是网络搜索研究的热点所在,在这其中,字符串匹配算法起着非常重要的作用,一个高效的字符串匹配算法,可以极大的提高搜索的效率和质量。 (请同时参照课本P53~54相关内容) 1.要理解next[j]=k 中,k的含意; (1)BF算法 假设有字符串 S=S1S2......S N P=P1P2......P M 其中(M

(2)KMP算法 为了解决上述的问题,KMP算法被发现。 KMP算法的思想如下。匹配过程中,出现不匹配时,S的指针不进行回朔(原地不动),将P尽可能地向后移动一定的距离,再进行匹配。 如图: (该图引用自互联网) 从上图中我们看到,当S移动到i,P到j的时候失配。这时候i不回朔,而只是将P 向前移动尽可能的距离,继续比较。 假设,P向右移动一定距离后,第k个字符P[k]和S[i]进行比较。 此时如上图,当P[j]和S[i]失配后,i不动,将P前移到K,让P[k]和S[i]继续匹配。现在的关键是K的值是多少? 通过上图,我们发现,因为黄色部分表示已经匹配了的结果(因为是到了S[i]和P[j]的时候才失配,所以S i-j+1S i-j+2…S i-1 = P1P2…P j-1,见黄色的部分)。所以有: 1、S i-k+1S i-k+2…S i-1 = P j-k+1P j-k+2…P j-1。 所以当P前移到K时,有: 2、S i-k+1S i-k+2…S i-1 = P1P2…P k-1。 通过1,2有 P j-k+1P j-k+2…P j-1 = P1P2…P k-1。 呵呵,此时我们的任务就是求这个k值了。。。 参考:https://www.doczj.com/doc/0112620940.html,/2008-09/122068902261358.html 2.求出k 值 按照课本的求法就可以处理。 课本是已知前j个元素的“前缀函数值”,如何求的j+1个元素的前缀函数值。这里有一个思路要发生转变的地方,把一个模式串分成两个部分,因为我们要找k使得P j-k+1P j-k+2…P j-1= P1P2…P k-1,而这本身就是一个模式匹配问题,所以把模式串的前边部分的子串当作“新的模式串”,这样就很容易理解为什么当t k!=t j时,t1…t next[k]-1 = t j-(next[k]-1)…t j-1了。因为这时候t k匹配失败,需要进一步移动模式子串,所以移动的位置就是next[k]。

模式匹配KMP算法实验步骤

一、问题描述 模式匹配两个串。 二、设计思想 这种由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现的改进的模式匹配算法简称为KM P算法。 注意到这是一个改进的算法,所以有必要把原来的模式匹配算法拿出来,其实理解的关键就在这里,一般的匹配算法: int Index(String S,String T,int pos)//参考《数据结构》中的程序 { i=pos;j=1;//这里的串的第1个元素下标是1 while(i<=S.Length && j<=T.Length) { if(S[i]==T[j]){++i;++j;} else{i=i-j+2;j=1;}//**************(1) } if(j>T.Length) return i-T.Length;//匹配成功 else return 0; } 匹配的过程非常清晰,关键是当‘失配’的时候程序是如何处理的?为什么要回溯,看下面的例子: S:aaaaabababcaaa T:ababc aaaaabababcaaa ababc.(.表示前一个已经失配) 回溯的结果就是 aaaaabababcaaa a.(babc) 如果不回溯就是 aaaaabababcaaa aba.bc 这样就漏了一个可能匹配成功的情况 aaaaabababcaaa ababc 这是由T串本身的性质决定的,是因为T串本身有前后'部分匹配'的性质。如果T为a bcdef这样的,大没有回溯的必要。

改进的地方也就是这里,我们从T串本身出发,事先就找准了T自身前后部分匹配的位置,那就可以改进算法。 如果不用回溯,那T串下一个位置从哪里开始呢? 还是上面那个例子,T为ababc,如果c失配,那就可以往前移到aba最后一个a的位置,像这样: ...ababd... ababc ->ababc 这样i不用回溯,j跳到前2个位置,继续匹配的过程,这就是KMP算法所在。这个当T[j]失配后,j应该往前跳的值就是j的next值,它是由T串本身固有决定的,与S串无关。 《数据结构》上给了next值的定义: 0 如果j=1 next[j]={Max{k|1aaab ->aaab ->aaab 像这样的T,前面自身部分匹配的部分不止两个,那应该往前跳到第几个呢?最近的一个,也就是说尽可能的向右滑移最短的长度。 到这里,就实现了KMP的大部分内容,然后关键的问题是如何求next值?先看如何用它来进行匹配操作。 将最前面的程序改写成: int Index_KMP(String S,String T,int pos) { i=pos;j=1;//这里的串的第1个元素下标是1 while(i<=S.Length && j<=T.Length) {

KMP算法实验

入 侵 检 测 试 验 实验名称:_ KMP算法实验专业班级: _ 网络工程13-01 学号:_ 姓名:

一、问题描述 模式匹配两个串。 二、设计思想 这种由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现的改进的模式匹配算法简称为KM P算法。 注意到这是一个改进的算法,所以有必要把原来的模式匹配算法拿出来,其实理解的关键就在这里,一般的匹配算法: int Index(String S,String T,int pos)//参考《数据结构》中的程序 { i=pos;j=1;//这里的串的第1个元素下标是1 while(i<=S.Length && j<=T.Length) { if(S[i]==T[j]){++i;++j;} else{i=i-j+2;j=1;}//**************(1) } if(j>T.Length) return i-T.Length;//匹配成功 else return 0; } 匹配的过程非常清晰,关键是当‘失配’的时候程序是如何处理的?为什么要回溯,看下面的例子: S:aaaaabababcaaa T:ababc aaaaabababcaaa ababc.(.表示前一个已经失配) 回溯的结果就是 aaaaabababcaaa a.(babc) 如果不回溯就是 aaaaabababcaaa aba.bc 这样就漏了一个可能匹配成功的情况 aaaaabababcaaa ababc 这是由T串本身的性质决定的,是因为T串本身有前后'部分匹配'的性质。如果T为a bcdef这样的,大没有回溯的必要。

改进的地方也就是这里,我们从T串本身出发,事先就找准了T自身前后部分匹配的位置,那就可以改进算法。 如果不用回溯,那T串下一个位置从哪里开始呢? 还是上面那个例子,T为ababc,如果c失配,那就可以往前移到aba最后一个a的位置,像这样: ...ababd... ababc ->ababc 这样i不用回溯,j跳到前2个位置,继续匹配的过程,这就是KMP算法所在。这个当T[j]失配后,j应该往前跳的值就是j的next值,它是由T串本身固有决定的,与S串无关。 《数据结构》上给了next值的定义: 0 如果j=1 next[j]={Max{k|1aaab ->aaab ->aaab 像这样的T,前面自身部分匹配的部分不止两个,那应该往前跳到第几个呢?最近的一个,也就是说尽可能的向右滑移最短的长度。 到这里,就实现了KMP的大部分内容,然后关键的问题是如何求next值?先看如何用它来进行匹配操作。 将最前面的程序改写成: int Index_KMP(String S,String T,int pos) { i=pos;j=1;//这里的串的第1个元素下标是1 while(i<=S.Length && j<=T.Length) {

字符串匹配的KMP算法

字符串匹配是计算机的基本任务之一。 举例来说,有一个字符串"BBC ABCDAB ABCDABCDABDE",我想知道,里面是否包含另一个字符串"ABCDABD"? 许多算法可以完成这个任务,Knuth-Morris-Pratt算法(简称KMP)是最常用的之一。它以三个发明者命名,起头的那个K就是著名科学家Donald Knuth。 我用自己的语言,试图写一篇比较好懂的KMP算法解释。 1.

首先,字符串"BBC ABCDAB ABCDABCDABDE"的第一个字符与搜索词"ABCDABD"的第一个字符,进行比较。因为B与A不匹配,所以搜索词后移一位。 2. 因为B与A不匹配,搜索词再往后移。 3. 就这样,直到字符串有一个字符,与搜索词的第一个字符相同为止。 4.

接着比较字符串和搜索词的下一个字符,还是相同。 5. 直到字符串有一个字符,与搜索词对应的字符不相同为止。 6. 这时,最自然的反应是,将搜索词整个后移一位,再从头逐个比较。这样做虽然可行,但是效率很差,因为你要把"搜索位置"移到已经比较过的位置,重比一遍。 7.

一个基本事实是,当空格与D不匹配时,你其实知道前面六个字符是"ABCDAB"。KMP算法的想法是,设法利用这个已知信息,不要把"搜索位置"移回已经比较过的位置,继续把它向后移,这样就提高了效率。 8. 怎么做到这一点呢?可以针对搜索词,算出一张《部分匹配表》(Partial Match Table)。这张表是如何产生的,后面再介绍,这里只要会用就可以了。 9. 已知空格与D不匹配时,前面六个字符"ABCDAB"是匹配的。查表可知,最后一个匹配字符B对应的"部分匹配值"为2,因此按照下面的公式算出向后移动的位数:

KMP算法实验报告

第四章 KMP算法 一实验目的: 熟悉串类型的实现方法,了解KMP算法的实现。

二概要设计: 1.实现KMP匹配算法 int KMPMatch(char *s,char *p) 2.对模式串进行求next操作 void getNext(char *p,int *next) 三详细设计: #include #include #include #include #define CHUNKSIZE 80 void getNext(char *p,int *next) { int j,k,x; next[0]=-1; j=0; k=-1; x=strlen(p); printf("模式串长%d\n",x); while(j

{ printf("%c:\n",p[j]); printf("next[%d]=%d\n",j,next[j]); } } int KMPMatch(char *s,char *p) { int next[100]; int i,j; i=0; j=0; getNext(p,next); while(i

(完整word版)KMP算法详解

KMP字符串模式匹配详解KMP字符串模式匹配通俗点说就是一种在一个字符串中定位另一个串的高效算法。简单匹配算法的时间复杂度为O(m*n);KMP匹配算法。可以证明它的时间复杂度为O(m+n).。 一.简单匹配算法 先来看一个简单匹配算法的函数: int Index_BF ( char S [ ], char T [ ], int pos ) { /* 若串S 中从第pos(S 的下标0≤pos

详解KMP算法中Next数组的求法

详解KMP算法中Next数组的求法 例如: 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数组的求解方法是:第一位的next值为0,第二位的next 值为1,后面求解每一位的next值时,根据前一位进行比较。首先将前一位与其next值对应的内容进行比较,如果相等,则该位的next 值就是前一位的next值加上1;如果不等,向前继续寻找next值对应的内容来与前一位进行比较,直到找到某个位上内容的next值对应的内容与前一位相等为止,则这个位对应的值加上1即为需求的next值;如果找到第一位都没有找到与前一位相等的内容,那么需求的位上的next值即为1。 看起来很令人费解,利用上面的例子具体运算一遍。 1.前两位必定为0和1。 2.计算第三位的时候,看第二位b的next值,为1,则把b和1对应的a进行比较,不同,则第三位a的next的值为1,因为一直比到最前一位,都没有发生比较相同的现象。 3.计算第四位的时候,看第三位a的next值,为1,则把a和1对应的a进行比较,相同,则第四位a的next的值为第三位a的next 值加上1。为2。因为是在第三位实现了其next值对应的值与第三位的值相同。

4.计算第五位的时候,看第四位a的next值,为2,则把a和2对应的b进行比较,不同,则再将b对应的next值1对应的a与第四位的a进行比较,相同,则第五位的next值为第二位b的next值加上1,为2。因为是在第二位实现了其next值对应的值与第四位的值相同。 5.计算第六位的时候,看第五位b的next值,为2,则把b和2对应的b进行比较,相同,则第六位c的next值为第五位b的next 值加上1,为3,因为是在第五位实现了其next值对应的值与第五位相同。 6.计算第七位的时候,看第六位c的next值,为3,则把c和3对应的a进行比较,不同,则再把第3位a的next值1对应的a与第六位c比较,仍然不同,则第七位的next值为1。 7.计算第八位的时候,看第七位a的next值,为1,则把a和1对应的a进行比较,相同,则第八位c的next值为第七位a的next 值加上1,为2,因为是在第七位和实现了其next值对应的值与第七位相同。

经典KMP算法(易理解)

KMP算法小结 Posted on 2011/06/14 by huangchao 主要看了这里,感觉讲的十分的不错,总结一下。 首先声明要搜索的串为S,设长度为n,要匹配的串为M,设长度为m. 先考虑暴力的算法,暴力的算法是遍历S的每一个字符,然后从这个字符开始和M串进行匹配。时间复杂度为O(nm). 怎么在此基础上进行优化?假设现在从某个位置(设为s)开始和M串进行匹配,如果匹配不成功,暴力算法是从这个位置的下一个位置(s+1)进行匹配,直观上来说就是匹配的字符串向后“滑动”了一位。 图1 能不能想办法让M向后移动的距离最大化?考虑最好的情况,如果和M匹配的S中的m个字符和M中的字符没有一个相等,那么能向右移动m位;考虑最坏的情况,比如上图,只能移动一位。 而KMP就是在这里做文章,让M串向后“滑动”的距离最大化。 图2

考虑上面的图,M中灰色部分已经和S的灰色部分匹配上了,而灰色部分后一个字符不匹配,则现在M要向后滑动,假设一直向后滑动,直到如图位置又和S再一次匹配上了,那么从这里我们可以得到如下的结论: ?A段字符串是M的一个前缀。 ?B段字符串是M的一个后缀。 ?A段字符串和B段字符串相等。 这样,如果暂时不考虑S,只看M的话,假设已经匹配的M的字串(即图中M 中灰色部分)为subM,则subM有个【相等】的【前缀】和【后缀】。而且M在遇到不匹配的时候可以直接滑动到使subM的前缀和subM的后缀重合的地方。而M向后滑动的时候,第一次subM的前缀和后缀重合意味着此时这个相等的subM的前缀和后缀的长度是最大的。 我们的任务就是要寻找subM的最长的前缀和后缀相等的串。 知道了这一点,离KMP的真谛也就不远了。现在结合这上面的图模拟一下KMP 算法的整个流程: ?将S串和M串从第一个字符开始匹配; ?如果匹配成功,则subM即灰色部分增加; ?如果不成功,则M向后滑动使滑动后的subM的前缀和滑动前的subM的后缀重合,再进行匹配,如果还不成功,则再次滑动M,直到匹配成功或者M 滑动到X处。如果到了X处,则从M串的起始位置进行匹配。 从上面的步骤可以知道,KMP的关键就是要知道当S串中的字符和M串中的字符不匹配时,S串要和M串中的哪个字符继续进行匹配。这个就是在利用状态机模型来解释KMP算法时的状态转移. KMP是通过一个定义了一个next数组,这个next数组保存了如果S中的字符和M中的字符不匹配时S要和M中的哪个字符重新进行匹配的坐标值。如图2中所示是例子,S中的X位置和M不匹配了,那么S要和M中A段后面的字符进行比较,从图中来看是M向后滑动了。 换句话说,next[i]总是保存了当M[i]不匹配时要从M[next[i]]处进行匹配,这个M[next[i]]可能会匹配,如果还不匹配?那么可能会在M[next[next[i]]]处匹配了。这里同时隐含着一个信息,就是i之前的一段字符和next[i]之前的一段字符是相同的,也就是M[0…i-1]相等的前缀和后缀。 现在考虑next[0],next[1]…next[i]都已经知道了,那么图示如下:

KMP算法源码

#define _CRT_SECURE_NO_DEPRECA TE #include #include #include #include using namespace std; #define N 100 void cal_next(char * str, int * next, int len) { int i, j; next[0] = 0; for (i = 1; i < len; i++) { j = next[i - 1]; while (str[j] != str[i] && (j > 0))//直到对称子串中再无最长前后缀 { j = next[j-1]; //或者在对称子串中找到一个之前满足条件的最长前缀 } if (str[i] == str[j]) { next[i] = j + 1; } else { next[i] = 0; } } } int KMP(char * str, int slen, char * ptr, int plen, int * next) { int s_i = 0, p_i = 0; int i; printf("%s\n",str); printf("%s\n",ptr); while (s_i < slen && p_i < plen) { if (str[s_i] == ptr[p_i]) {

s_i++; p_i++; continue; } else { if (p_i == 0) { s_i++; } else { p_i = next[p_i-1]; //取当前匹配不到之前的字符串的最大相等前缀的最后一个字符 } } for (i = 0; i < s_i - p_i; i++) { putchar(' '); } printf("%s\n",ptr); } return (p_i == plen) ? (s_i - plen) : -1;//返回第一次找到子串的下标位置 } int main() { char str[N] = { 0 }; char ptr[N] = { 0 }; int slen, plen; int next[N]; int ret; printf("请输入主串:"); scanf("%s",str); printf("请输入模式串:"); scanf("%s",ptr); slen = strlen(str); plen = strlen(ptr); cal_next(ptr, next, plen); printf("\nnext:");

KMP算法步骤分析

KMP算法步骤剖析 注:先计算好next值进而推出nextval数值,可在CSND上找到求解方法。 对于上述KMP算法第趟匹配,有一定的难度!接下来,我来分析一下。 先给主串和模式串进行编号: 主串 a b c a a b b a b c a b a a c b a c b a 编号i: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 模式串 a b c a b a a 编号j:1 2 3 4 5 6 7 (1)第一趟排序 主串t: a b c a a b b a b c a b a a c b a c b a 模式串p: a b c a b a a i=5 j=5 时匹配失败 取模式串p第五位的nextval值。nextval=1

故下一趟排序将主串的第i=5位与模式串的第nextval=1位进行比 较。 即得到第二趟排序: (2)第二趟排序 主串t: a b c a a b b a b c a b a a c b a c b a 模式串p: a b c a b a a i=7 j=3 时匹配失败 取模式串p第三位的nextval值。nextval=1 故下一趟排序将主串的第i=7位与模式串的第nextval=1位进行比 较。 即得到第三趟排序: (3)第二趟排序 主串t: a b c a a b b a b c a b a a c b a c b a 模式串p: a b c a b a a i=7 j=1 匹配失败 取模式串p第1位的nextval值。nextval=0 当nextval=0时,主串要向后面移动一位 故下一趟排序将主串的第i=7+1=8位与模式串的第1位进行比较。 (4)第二趟排序 主串t: a b c a a b b a b c a b a a c b a c b a 模式串p: a b c a b a a i=15 j=8 匹配成功!

KMP算法考题

KMP算法是在最近这两年的软件设计师考试中才出现的。2次都是让求Next函数的序列(其实是)。先看看题吧。 (2011年下半年上午题) (2012年上半年上午题)

其实做这个题很简单,我先说说这个题里的各种概念。 给定的字符串叫做模式串T。j表示next函数的参数,其值是从1到n。而k则表示一种情况下的next函数值。p表示其中的某个字符,下标从1开始。看等式左右对应的字符是否相等。 好了,开始做题了。 首先,要把字符串填入到一个表格中:(拿第一个题为例) 将j导入next函数,即可求得, j=1时,next[0]=0; j=2时,k的取值为(1,j)的开区间,所以整数k是不存在的,那就是第三种情况,next[2]=1; j=3时,k的取值为(1,3)的开区间,k从最大的开始取值,然后带入含p的式子中验证等式是否成立,不成立k取第二大的值。现在是k=2,将k导入p的式子中得,p1=p2,即“a”=“b”,显然不成立,

舍去。k再取值就超出范围了,所以next[3]不属于第二种情况,那就是第三种了,即next[3]=1; j=4时,k的取值为(1,4)的开区间,先取k=3,将k导入p的式子中得,p1p2=p2p3,不成立。再取k=2,得p1=p3,成立。所以next[4]=2; j=5时,k的取值为(1,5)的开区间,先取k=4,将k导入p的式子中得,p1p2p3=p2p3p4,不成立。再取k=2,得p1p2=p3p4,不成立。再取k=2,得p1=p4,成立。所以next[4]=2; j=6时,k的取值为(1,6)的开区间,先取k=5,将k导入p的式子中得,p1p2p3p4=p2p3p4p5,不成立。取k=4,得p1p2p3=p3p4p5,不成立。再取k=3,将k导入p的式子中得,p1p2=p4p5,成立。所以next[4]=3; j=7时,k的取值为(1,7)的开区间,先取k=6,将k导入p的式子中得,p1p2p3p4p5=p2p3p4p5p6,不成立。再取k=5,得 p1p2p3p4=p3p4p5p6 ,不成立。再取k=4,得p1p2p3=p4p5p6 ,成立。所以next[4]=4;

KMP算法演算过程(讲述内容)

KMP中next数组以及nextval数组的求法。明确传统模式匹配算法的不足,明确next数组需要改进之外。其中,理解算法是核心,会求数组是得分点。不用我多说,这一节内容是本章的重中之重。可能进行的考查方式是:求next和nextval 数组值,根据求得的。 KMP算法即Knuth-Morris-Pratt算法,是模式匹配的一种改进算法,因为是名字中三人同时发现的,所以称为KMP算法。因为偶然接触到有关KMP的问题,所以上网查了一下next数组和nextval数组的求法,却没有找到,只有在CSDN 的资料文件里找到了next数组的简单求法(根据书上提供的程序也可以求到,但一般在课堂讲解的时候,学生难以理解,所以希望以更容易理解的形式来讲解),那位高人说时间关系,先讲到这里,于是讲完了next数组就功成身退了。BS的同时,自己研究了下nextwal数组,发现了其中的简易规律,并写了出来,希望能对需要快速理解KMP中nextval的求法的朋友有所帮助。 int get_nextval(SString T,int &nextval[ ]){ //求模式串T的next函数修正值并存入数组nextval。 i=1; nextval[1]=0; j=0; while(i

KMP算法的理论推导

改进的模式匹配算法的理论分析 设 T = t0 t1 … t s-1 t s t s+1 t s+2 … t s+j-1 t s+j t s+j+1 … t n-1 P = p0 p1 p2 … p j-1 p m-1. 若在匹配过程中出现了如下情况: t s t s+1t s+2… t s+j-1= p0p1p2… p j-1,(1)但t s+j ≠ p j.也就是说,在匹配过程出现了: T t0 t1 … t s-1t s t s+1t s+2… t s+j-1t s+j t s+j+1… t s+m-1 … t n-1 ‖ ‖ ‖ … ‖ ? P p0p1p2 … p j-1p j p j-1… p m-1 则本次匹配失败. 由朴素的模式匹配算法,我们需要下一趟匹配,即需要验证下式是否成立: t s+1t s+2… t s+j-1 t s+j … t s+m?= p0p1 … p j-2p j-1… p m-1(2)如果(2)式成立,则匹配成功,返回s+1;否则需要再下一趟的匹配:t s+2t s+3… t s+j-1t s+j… t s+m+1?= p0p1… p j-3p j-2 …p m-1 (2')以此类推. 下面给出两个互逆的条件 p0p1… p j-2 = p1p2 …p j-1 (3) p0p1… p j-2 ≠p1p2 …p j-1 (3')显然,这两个条件能且只能满足一个.下面并分情况讨论:【1】如果(3) 式成立,则由(1) (2) (3) 式,可以断定p0 p1 …p j-2 = t s+1 t s+2 … t s+j-1成立,即在(3)式条件下,对(2) 式的验证只需要从p j-

kmp算法详解

引记 此前一天,一位MS的朋友邀我一起去与他讨论快速排序,红黑树,字典树,B树、后缀树,包括KMP算法,唯独在讲解KMP算法的时候,言语磕磕碰碰,我想,原因有二:1、博客内的东西不常回顾,忘了不少;2、便是我对KMP算法的理解还不够彻底,自不用说讲解自如,运用自如了。所以,特再写本篇文章。由于此前,个人已经写过关于KMP算法的两篇文章,所以,本文名为:KMP算法之总结篇。 本文分为如下六个部分: 1. 第一部分、再次回顾普通的BF算法与KMP算法各自的时间复杂度,并两相对照各 自的匹配原理; 2. 第二部分、通过我此前第二篇文章的引用,用图从头到尾详细阐述KMP算法中的 next数组求法,并运用求得的next数组写出KMP算法的源码; 3. 第三部分、KMP算法的两种实现,代码实现一是根据本人关于KMP算法的第二篇文 章所写,代码实现二是根据本人的关于KMP算法的第一篇文章所写; 4. 第四部分、测试,分别对第三部分的两种实现中next数组的求法进行测试,挖掘其 区别之所在; 5. 第五部分、KMP完整准确源码,给出KMP算法的准确的完整源码; 6. 第六步份、一眼看出字符串的next数组各值,通过几个例子,让读者能根据字符串 本身一眼判断出其next数组各值。 力求让此文彻底让读者洞穿此KMP算法,所有原理,来龙去脉,让读者搞个通通透透(注意,本文中第二部分及第三部分的代码实现一的字符串下标i 从0开始计算,其它部分如第三部分的代码实现二,第五部分,和第六部分的字符串下标i 皆是从1开始的)。 在看本文之前,你心中如若对前缀和后缀这个两个概念有自己的理解,便最好了。有些东西比如此KMP算法需要我们反复思考,反复求解才行。个人写的关于KMP算法的第二篇文章为:六(续)、从KMP算法一步一步谈到BM算法;第一篇为:六、教你初步了解KMP算法、updated(文末链接)。ok,若有任何问题,恳请不吝指正。多谢。 第一部分、KMP算法初解 1、普通字符串匹配BF算法与KMP算法的时间复杂度比较

大学课件-KMP算法

这里有两种KMP算法的详解~大家可以参考 KMP字符串模式匹配详解KMP字符串模式匹配通俗点说就是一种在一个字符串中定位另一个串的高效算法。简单匹配算法的时间复杂度为O(m*n);KMP匹配算法。可以证明它的时间复杂度为O(m+n).。 一.简单匹配算法 先来看一个简单匹配算法的函数: int Index_BF ( char S [ ], char T [ ], int pos ) { /* 若串S 中从第pos(S 的下标0≤pos

j=0 起比较S[i+j] 与T[j],若相等,则在主串S 中存在以i 为起始位置匹配成功的可能性,继续往后比较( j逐步增1 ),直至与T串中最后一个字符相等为止,否则改从S串的下一个字符起重新开始进行下一轮的"匹配",即将串T向后滑动一位,即i 增1,而j 退回至0,重新开始新一轮的匹配。 例如:在串S=”abcabcabdabba”中查找T=” abcabd”(我们可以假设从下标0开始):先是比较S[0]和T[0]是否相等,然后比较S[1] 和T[1]是否相等…我们发现一直比较到S[5] 和T[5]才不等。如图: 当这样一个失配发生时,T下标必须回溯到开始,S下标回溯的长度与T相同,然后S 下标增1,然后再次比较。如图: 这次立刻发生了失配,T下标又回溯到开始,S下标增1,然后再次比较。如图: 这次立刻发生了失配,T下标又回溯到开始,S下标增1,然后再次比较。如图:

相关主题
文本预览
相关文档 最新文档