我不喜欢阅读长篇的还夹杂着数学公式的逻辑分析,所以看到很多讲解KMP算法的资料,觉得挺伤脑筋的,但花了挺长时间尝试学习KMP算法,KMP算法的思路是明白了,但是编程实现它可能还有点晕,参考了一些资料,它们的next数组有几种版本,有些是从下标0开始,有的是从下标1开始。

   一、我的尝试版

   现在有一个子串t="aabacaaabaabba"

下标 0 1 2 3 4 5 6 7 8 9 10 11 12 13
子串 a a b a c a a a b a a b b a
next 0 0 1 0 1 0 1 2 2 3 4 2 3 0

   当在t[0]处匹配失败时,下一次匹配还是从0开始匹配,所以next[0]=0.

   当在t[1]处匹配失败时,t[1]的前缀只有一个a,所以下一次匹配还是从下标0处开始,因而next[1]=0。

   当在t[2]处匹配失败时,t[2]的前缀有一对元素匹配,所以下一次匹配从下标1处开始,因而next[2]=1。

   当在t[3]处匹配失败时,t[3]的前缀没有对称元素,所以下一次匹配从下标0处开始,因而next[3]=0。

   当在t[4]处匹配失败时,t[4]的前缀有一对元素对称,所以下一次匹配从下标1处开始,因而

next[4]=1。

   当在t[5]处匹配失败时,t[5]的前缀没有元素对称,所以下一次匹配从下标0处开始,因而

next[5]=0。

   当在t[6]处匹配失败时,t[6]的前缀有一对元素对称,所以下一次匹配从下标1处开始,因而

next[6]=1。

   当在t[7]处匹配失败时,t[4]的前缀有两对元素对称,所以下一次匹配从下标2处开始,因而

next[7]=2。

   同理,我们可以分析出后面的next元素。

   next数组填充元素过程我们知道了,但是还只是停留在理论上,真正地编程实现算法还需要更多考虑。下面是结合我自己的想法实现的求next数组的算法。

void get_next(char *t, char *next){    int i, j;    int tlen = strlen(t);    i = 1;    j = 0;    next[0] = 0;    next[1] = 0;    while(i < tlen)    {        if(t[i] == t[j])        {            i++;    j++;            next[i] = j;        }        else{            if(j == 0)            {                i++;                next[i] = j;            }            j = next[j];        }    }}

   循环执行的次数与子串的长度和字符重复情况有关系,时间复杂度为O(n)。

   匹配算法如下:

int get_index(char *s, char *t, int pos){    char *next;    int slen, tlen, i, j;    slen = strlen(s);    tlen = strlen(t);    next = (char*)malloc(sizeof(tlen)+1);    get_next(t, next);    i = pos;    j = 0;    while(i < slen && j < tlen)    {        if(s[i] == t[j] )        {            i++;   j++;        }        else{            if(j==0)                i++;            j = next[j];        }    }    if(j >= tlen)        return (i-tlen);    else        return 0;}

   时间复杂度为O(n+m)。

   二、教科书版

   我们数据结构课程时,用的课本是清华大学出版社出版,严蔚敏和吴伟民编写的数据及结构(C语言版),真心没搞懂分析过程。

   求next数组算法:

void get_nextval(char *t, int nextval){    i= 0;    nextval[1] = 0;    j = 0;    while(i < t[0])    {        if(j ==0 || t[i] == t[j])        {            i++;    j++;            if(t[i] != t[j])                nextval[i] = j;            else                nextval[i] = nextval[j];        }        else            j = nextval[j];    }}

   那本书上给出了两个求next数组的算法,不过get_nextval更优。t[0]存放的是t串的字符个数。下面的s串的s[0]的字符个数。

   模式匹配算法:

int index_KMP(char *s, char *t, int pos){    i = pos;    j = 1;    while(i <= s[0] && j <= t[0])    {        if(j == 0 && s[i] == t[j])        {            i++;    j++;        }        else            j = next[j];    }                                                                                    if(j > t[0])        return i = t[0];    else        return 0;}

   三、一个模式匹配程序

#include
#include
#include
void get_next(char *t, char *next){ int i, j; int count = 0; int tlen = strlen(t); i = 1; j = 0; next[0] = 0; next[1] = 0; while(i < tlen) { count++; if(t[i] == t[j]) { i++; j++; next[i] = j; } else{ if(j == 0) { i++; next[i] = j; } j = next[j]; } } next[tlen] = '\0'; printf("count:%d\n", count);}int get_position(char *s, char *t, int pos){ char *next; int slen, tlen, i, j; slen = strlen(s); tlen = strlen(t); next = (char*)malloc(sizeof(tlen)+1); get_next(t, next); i = 0; j = 0; while(i < tlen) { printf("next[%d] = %d\n", i, next[i]); i++; } i = pos; while(i < slen && j < tlen) { if(s[i] == t[j] ) { i++; j++; } else{ if(j==0) i++; j = next[j]; } } if(j >= tlen) return (i-tlen); else return 0;}void main(){ char *s = "aaabaaaaaaaabbbababacbacbab"; char *t = "aabacaaabaabba"; int pos, index; index = get_position(s, t, pos); printf("index: %d\n", pos);}