匹配算法(网络摘抄)-KMP

来源:互联网 发布:mac word 繁简转换 编辑:程序博客网 时间:2024/06/18 16:05

所谓匹配就是在字符串里找另一个字串。简单匹配算法的时间复杂度为O(m*n),而KMP匹配算法可以证明它的时间复杂度为O(m+n)。

一、简单匹配算法

int Index_BF ( char S [ ], char T [ ], int pos ) { /* 若串 S 中从第pos(S 的下标0≤pos<StrLength(S))个字符起存在和串 T 相同的子串,则称匹配成功,返回第一个这样的子串在串 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相同的子串}

此算法的思想是直截了当的:将主串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中相应的字符匹配了。函数返回TS中的起始下标3。如图:


二、KMP匹配算法

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

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

定义

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

(2)next[j]= -1  意义:模式串T中下标为j的字符,如果与首字符相同,且j的前面的1~k个字符与开头的1~k个字符不等(或者相等但T[k]==T[j])(1k<j)。如:T=”abCabCad” next[6]=-1,因T[3]=T[6]

(3)next[j]=k   意义:模式串T中下标为j的字符,如果j的前面k字符与开头的k个字符相等,且T[j] != T[k] 1k<j)。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].1k<j;

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

例:求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]

 

下标

0

1

2

3

4

5

6

7

8

T

a

b

a

b

c

a

a

b

c

next

-1

0

-1

0

2

-1

1

0

2

只要理解了next[3]=0,而不是=1next[6]=1,而不是= -1next[8]=2,而不是= 0,其他的都容易理解。

next[n]意义

 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 >0k<n, 表示,S[m]的前k个字符与T中的开始k个字符已经间接比较相等了,下一次比较S[m]T[k]相等吗?

4.      其他值,不可能。

代码段:

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;        } }