花了半天的来学习KMP算法,勉强算是看懂并写出来了。
看了很多大佬的解析,感觉比较舒服的就是https://www.cnblogs.com/yjiyjige/p/3263858.html的文章了
文章中有很多的插图方便大家更好的理解next数组的定义以及KMP算法的实现
next数组的每一个元素值就是当P[j] != T[i]时,j指针的下一步移动位置
这里的i和j分别为主串和模式串在匹配过程中的下标。
算法思想:
1.首先初始化 :
size_t tLen = strlen(t), j = 1;//模式串指针
next[0] = 0;
size_t k = 0; //模式串前缀指针
这里获取了模式串的长度tLen,next[0] = 0是为什么呢?
next[i]是当P[j] != T[i]时,模式串j指针的下一步移动位置
所以当p[0] != T[0]也就是第一个字符就没有匹配的情况下,比如主串“abc”,模式串“bc”
此时模式串还能怎么移,因为模式串指针已经就在开头了啊,很多其他算法将next[0]=-1,我将其赋值为0
意思是继续匹配这个位置
我听清华大学邓俊辉老师讲的时候他讲next[0] = -1称为通配符,就是可以和任何字符匹配的字符
所以当看上面的代码可以知道t<0也就是 t== -1的时候,可以直接N[++j] = ++t;
因为next[0]已经定义,所以j从1开始,不过k依然从0开始,
是因为我们要找一个模式串在当前位置的最大前缀的下标k来和相应长度的后缀匹配
2.开始遍历模式串获取next数组的值
这里比较关键的就是当t[j] != t[k]时,为什么k=next[k]???
因为当失配的时候,k就应该转移到之前匹配的位置继续匹配!!!
这个很抽象,感觉可以举几个实例来理解!!!
while (j < tLen) {
if (k == 0 || t[j] == t[k]) {
next[j++] = k++;
}
else {
k = next[k];
}
}
接下来粘贴的代码是网上搜到的next数组比较简洁的代码
void GetNext(charT[],intnext[]){
next[1]=0;
j=1;k=0;
while(j<T[0])
if((k==0)||(T[j]==T[k])){
next[++j]=++k;
}
else k=next[k];
}
下面的代码是我自己理解并写出来的,虽然看上去和别人写的一样。。。
void getNext(char*t, int *next) {
size_t tLen = strlen(t), j = 1;//模式串指针
next[0] = 0;
size_t k = 0; //前缀串指针
while (j < tLen) {
if (k == 0 || t[j] == t[k]) {
next[j++] = k++;
}
else {
k = next[k];
}
}
}
这两段代码的不同之处在于:第一段代码的T[0]位置存储了模式串T的长度,而我写的这段代码就是C语言里面的字符串数组
其他基本一样。因为我的next函数有些不同,所以我的KMP函数也有些区别!!!
int index_KMP(char *s, char *t) {
size_t sLen = strlen(s), tLen = strlen(t);
int i = 0, j = 0;//i为主串位置,j为模板串下标
while (i < sLen && j < tLen) {
if (j == 0 || s[i] == t[j]) {
i++;
j++;
}
else j = next[j];
}
if (j >= tLen) {
return i - tLen;
}
else
{
return -1;
}
}
测试代码和结果:
int main() {
char s[] = "abfbcacbabcabc";
char t[] = "abcabc";
getNext(t, next);
int pos = index_KMP(s, t);
printf("模式串%s在主串%s的下标是%d\n", t, s, pos);
system("pause");
return 0;
}
先说这么多吧,说实话,我对这个算法的理解也比较浅,欢迎各位评论指教!!!