详情见代码
#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
void prefix_table(char [],int ,char []);
void Next(char [],int ,char [] );
bool KMP_search(char [],int ,char [],char [],int ,int *);
//该函数求的是pattern字符串的前缀表pre[]
void prefix_table(char pattern[],int len,char prefix[]){
/*
前缀表prefix[]的第一个值一定为0,即pre[0]=0;
取两个下标,i,j,i从1开始,j从0开始
1.当i,j所对应的字符相等时,i++,j++
2.当i与j所对应的字符不相等时,又分为两种情况:
a.当j不是指向第一个位置即j=0时,则j=prefix[j-1],此时会继续比较,如果i,j所指向的字符还是不相等,则继续将j=prefix[j-1],直到j=0进入b情况
b.当j=0时,此时j=prefix[j-1]中的j-1越界,所以不可以做a情况中的操作,即直接将prefix[i]=0,,并且将i+1
*/
int i=1,j=0;
prefix[0]=0;
while(i<len){
if(pattern[i]==pattern[j]){
prefix[i]=j+1;
i++;
j++;
}
else{
if(j!=0){
j=prefix[j-1];
}
else{
prefix[i]=0;
i++;
}
}
}
}
//该函数得到最终的next数组(解释一下next数组时什么,即当主串中第i位与待匹配串第j位发生失配时,主串i不动,j下次取值的值的集合即为next[])
//这里的len是prefix[]的长度,即pattern字符串的长度
void Next(char prefix[],int len,char next[]){
/*
如何从prefix[]变成next[]?
即将prefix[]整体向后移一步,并在第一位赋值-1,即next[0]=-1;
*/
int i;
for(i=0;i<len;++i){
if(i==0) {
next[i]=-1;
continue;
}
next[i]=prefix[i-1];
}
}
//KMP算法,其中len1代表主串长度,len2代表next[]长度(注:pattern长度等于prefix[]长度等于next[]长度)
bool KMP_search(char text[],int len1,char pattern[],char next[],int len2,int *pVal){
/*
首先由前面步骤可得待匹配串的next[];
i指向主串第一个字符,j指向待匹配串第一个字符
1.当i,j所指向的字符相等时,i++,j++
2.当i,j所指向的字符不相等时,分为两种情况:
a.当j不是指向pattern的第一个位置时,j=next[i],然后再进行比较,如果此时i,j所指向的字符还是不相等,那么j继续等于next[j],直到j=0,进入b情况
b.当j=0时,此时也代表了待匹配串的第一个字符与当前字符不匹配,所以i,j同时加1
*/
int i=0,j=0;
while(i<len1&&j<len2){
if(text[i]==pattern[j]){
i++;
j++;
}
else{
if(j!=0) {
j=next[j];
}
else{
i++;
}
}
}
if(j==len2){
*pVal=i-len2; //最终返回的i-len2+1即为一开始匹配的字符位置
return true;
}
return false;
}
int main(){
char text[]="adbcefda";
char pattern[]="bcef";
int val;
int m=sizeof(text)-1;
int n=sizeof(pattern)-1;
char pre[n],next[n]; //pre[]存放前缀表的值,next[]存放的是最终需要的值
prefix_table(pattern,n,pre);
Next(pre,n,next);
if(KMP_search(text,m,pattern,next,n,&val)){
printf("匹配出现在第%d个位置",val+1);
}
else{
printf("主串中不存在与待匹配串相匹配的子串");
}
return 0;
}
/*前缀表如何求?
例如,字符串string[]="abcabcd";
其所有前缀集合为:
"每一行序列中最大公共前后缀放入前缀表中",即prefix[]={0,0,0,1,2,3,0}
0 a
0 ab
0 abc
1 abca
2 abcab
3 abcabc
0 abcabcd
*/
kmp算法的核心是求得最大公共前后缀的值,那么如何求的最大公共前后缀呢,这里附上我笔算的一段内容