这个算法很经典,我花了一个下午加一个晚上才搞明白!我佩服想出这个算法的人精湛而严密的数学思维!向他们致敬!这个算法,应该是我到目前为止见过的最美丽的算法了。
#include <stdio.h>
#include <string.h>
#include <iostream>
using namespace std;
void get_next(char T[],int next[],int len_T)//获取next数组的方法,T[]子串
{
int i,j;//同样定义两个指针
i=2;//i在后面,i现在虽然为2,实则考察的是i+1,即3,请读者自行领会
j=1;//j在前面
next[1]=0;//这是必然的
next[2]=1;//这是必然的
while(i<len_T)//注意,这里是<而不是<=,因为每到i,其实是考察的i+1的next的值!
{
if(T[i]==T[j])//如果相等,就++,然后next[i]就是j
{
i++;
j++;
next[i]=j;//这个赋值语句暗藏很多数学道理,读者请自行领悟,具体证明见严蔚敏83页公式推导论证
}
else
{
if(j==1)//说明j已经回溯到尽头了,此时j为1,因此i必须前进一格,对应于严蔚敏81页公式4-5中的其他情况,此时找不到这个k,因此置next[i]为1
{
i++;
next[i]=1;
}
else//这个说明还没有回溯到尽头,所以就继续回溯呗!
j=next[j];
}
}
}
int KMP(char S[],char T[],int pos,int next[],int len_S,int len_T)//KMP算法主体部分,S为主串,T为子串,pos是规定从主串下标为pos的地方开始模式匹配
{
int i,j;//i,j分别为主串和子串的指针
i=pos,j=1;
while(i<=len_S&&j<=len_T)//遍历主串和子串
{
if(S[i]==T[j])//如果相等,i和j都++
{
i++;
j++;
}
else//如果不等,就要看j的值了
{
if(j==1)//如果此时回溯到尽头,i就必须++
i++;
else
j=next[j];//否则就回溯呗
}
}
if(j>len_T)//这个要理解,如果我大于T[0],说明我此时已经全部匹配成功啦,i就回退T的长度,就是匹配成功字符串的第一个数组下标
return i-len_T;
return -1;//匹配不成功就返回-1
}
int main()
{
char c,S[100],T[100];
int next[100],ret,i,len_S,len_T;
i=1;
cout<<"请输入主串,以回车结束!"<<endl;
/*下标从1开始进行输入S*/
c=getchar();
while(c!='\n')
{
S[i++]=c;
c=getchar();
}
S[i]='\0';//C语言基础知识
len_S=i-1;//考察字符串基本功
/*下标从1开始进行输入T*/
i=1;
cout<<"请输入子串,以回车结束!"<<endl;
c=getchar();
while(c!='\n')
{
T[i++]=c;
c=getchar();
}
T[i]='\0';
len_T=i-1;
get_next(T,next,len_T);
ret=KMP(S,T,1,next,len_S,len_T);
if(ret!=-1)
cout<<"首次出现子串的下标为"<<ret<<"!"<<endl;
else
cout<<"找不到!"<<endl;
return 0;
}
