KMP算法

来源:互联网 发布:mac 没用 电池不充电 编辑:程序博客网 时间:2024/05/17 22:05

今天看了http://blog.csdn.net/chrython/archive/2006/10/05/1321894.aspx 中关于KMP算法的讨论,该文写得很好,感觉有些地方没有说明白 ,因此写下该文中没有说明白的地方.

 

 

KMP 算法:

KMP算法主要使用了一个模式串的next数组用来指定当模式不匹配时模式串应该回到的索引位置  数组大小为模式串的长度(strlen), 它可以将普通的字符串查找效率从O(M*N)减少到O(M+N).

 

next[] 用来定位当模式不匹配时模式串应该回到的索引位置.

next[] 的取值K的范围为 -1 <= k < j(j为当前不匹配时模式串的位置索引)

 

²  K =-1   表示原串S 索引前进一步 模式串回到索引位置0

²   K =0   表示原串 S 索引不前进   模式串回到索引位置0

²   0<K<j   表示原串 S 索引不前进   模式串回到索引位置K

 

next值是根据模式串来计算的.

next值的计算是基于这样一个事实:当原串与模式串匹配到不相等时, 模式串当前位置的前面元素是 与 原串当前位置前面的元素是相同的.

 

设原串S =  ; 模式串 P =    (0 < m<= n);

设当匹配到S, P的位置i ,j 时不相等也即S[i] != P[j] 并且 P[0]…P[j-1]==S[i-j]… S[i-1] .

 

1.      j=0时令其next-1  表示SP不匹配时, S 因该向前进一步 ,而P此时已在位置0(j=0)

2.      j>0时,此时有两种情况:

a)     如果P[j] ==P[0], 这时隐含了P[0] != S[i], 这时就需要决定S需不需要向前移动了显然当P[j]前面 1--K个字符与开始的1--K个字符相同(1<= K < j)并且P[K] != P[j],此时S不需要移动 而且P将会回到位置K next值为K . 否则 S需要向前移动一步,而P回到位置0 next值为-1

b)     这里P[j] != P[0]意味着P[0] S[i] 有可能相同,此时S肯定不会移动, 对于当P[j]前面有1-K个字符与开始1--K个字符相同(1<= K < j并且 P[K] != P[j] 此时P将会回到位置K next值为K  否则为 0

 

从上面可以得出两个对编写算法很重要的等式:1、P[k] == P[j] next[j] =next[k]; 2、P[k] != P[j]  next[j] = k. (这里: 1<= K <j) .   原因: P[K] == P[j]时 隐藏了P[k] != S[i] 又因为P[k]前面的k个字符与P[j]前面的K个字符是相同的,因此P[0]P[k] 与 从p[k+1] P[j]是完全相同的,S[i]P[j]比较不相等时,假设这是回到P[k],P[k] == p[j]!= S[i],同样是得到不匹配的结果, 此时模式串又会回溯, 事实上当存在P[k] == P[j]时模式串根本就不会比较到P[j] 当比较到P[k]时模式串P就会回溯, 这可能将会导致S向前进一步.

 

下面是KMP算法的三个发现者写的求next数组的代码 写得很好

 

voidgetNext( const char* T,  int next[])

{                         

                  intj =  0;

                   int k = -1;// K的最小值

                   next[0] = -1;

                  

                   while(T[j] != ‘/0’)

                   {// 模式串迭代

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

                   {//T[K ] T[j] 相等时,测试下一个是否相等如果相等 则 next[j+1] =

                       // next[k+1] 否则next[j+1] =K+1.

                            // K==-1时表示了 T[j] == T[0] T[0] !=T[j-1]  T[0]T[1] != T[j-2]T[j-1]...

                                     ++k;

                                     ++j;

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

                                               next[j]= k;

                                     else

                                               next[j]= next[k];

  }else{

// K!= -1 T[K] 不等于T[J]

         k = next[k];  //直接 k=-1

}

}

}

 

 

小结: 实际上有 next[0] = -1 next[1] = 0;

 k == -1, T[j] == T[0], 且 T[0]T[1]... != ....T[j-2]T[j-1]

 k ==  0, T[j] !=  T[0], 且 T[0]T[1]... != ....T[j-2]T[j-1]

 K   >  0, T[j] !=  T[0], 且 T[0]T[1]...T[k] ==T[j-K-1] ....T[j-2]T[j-1] 且T[k] != T[j]

 

 

 

 

#include<stdio.h>

#include<string.h>

#include<stdlib.h>

 

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

{

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

     int j = 0, k = -1;

     next[0] = -1;

     while ( T[j] != '/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 = -1;   

}

     }

}

 

 

// KMP算法

int KMP(const char* source, const char* pattern,int next[])

{

int i=0;

int j=0;

int plen = strlen(pattern);

int slen = strlen(source);

while(j<plen && i<slen)

{

if(source[i] != pattern[j])

{

if(next[j] == -1)

{

i++;

j=0;

}else if(next[j] == 0)

j=0;

else

j= next[j];

}

else

{

i++;

j++;

}

}

 

if(j!=plen)

return -1;

else

return i-j;

}

int main()

{

char s[] = "aaaaaaaaaaaaaaaavdaaaaaaaaaaaaaaaaaasaaaaaab";

char p[] = "aaaaaaaaaaaaaab";

 

int len = strlen(p);

int *next= (int *)malloc(sizeof(int) *len);

getNext(p,next);

//printf("%s /n",strstr(s,p));

printf("%d/n",KMP(s,p,next));

free(next);

return 0;

}

 

 

 

 

 

 

 

 

 

 

原创粉丝点击