在了解KMP算法之前,我想想说说朴素算法
从主串的第pos个字符起和模式串的第一个字符进行比较
相等,就继续比较后面的
不相等,就从主串的下一个字符起重新和模式串的第一个字符尝试匹配
以此类推一直到匹配成功
朴素算法,易于理解和实现,但是效率很低,主串中的指针i有回溯
在最坏情况下,就是每一趟都是模式串的最后一个字符处出现失配的状态,一共的比较次数是m*(n-m+1),一半情况下n>>m所以时间复杂度O(m*n)
例如:主串000000000000000000000000000000000000000001
模式串00001
用朴素算法就不方便了
然后KMP算法就闪亮登场了
它对朴素算法进行的改进其实就是,主串的指针i不再回溯。而是利用已经得到的部分,将模式串尽可能向右滑动尽可能远的距离,再继续比较
滑动的距离是最大相同真前缀和真后缀的长度
为了知道失配时候,将从哪一个位置开始再次匹配,需要定义一共和模式串一样长的next【】数组
#include<stdio.h>
#include<malloc.h>
#include <stdlib.h>
#include<string.h>
#define ok 1
#define error 0
#define maxlen 40
int next[maxlen],nextval[maxlen];
int StrAssign(char s[],char c[])//创建一个字符串
{
int a;
s;
a=strlen(c);
if(a>maxlen)
return error;
else
s[0]=a;
strcpy(&*(s+1),c);
return ok;
}
int printstr(char s[])//输出一个字符串
{
int a;
a=strlen(s);
for(int i=1;i<=a;i++)
{
printf("%c",s[i]);
}
printf("\n\n");
}
int index_KMP(char s[],char t[],int pos)
{
int 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;
}
int index_next(char s[])//求next函数
{
int i=1,j=0;
next[1]=0;
while(i<s[0])
{
if(j==0||s[i]==s[j]){++i;++j;next[i]=j;}
else j=next[j];
}
printf("next 函数的值是");
for(int i=1;i<strlen(s);i++)
printf("%d",next[i]);
printf("\n\n");
}
int index_nextval(char s[])//求nextval函数 (KMP的改进算法)
{
int i=1,j=0;
nextval[1]=0;
while(i<s[0])
{
if(j==0||s[i]==s[j]){++i;++j;next[i]=j;
if(s[i]!=s[j]) nextval[i]=j;
else nextval[i]=nextval[j];}
else j=nextval[j];
}
printf("nextval 函数的值是");
for(int i=1;i<strlen(s);i++)
printf("%d",nextval[i]);
printf("\n\n");
}
int main()
{
char ss1[40],ss2[40];
char s1[40],s2[40];
gets(s1);
gets(s2);
StrAssign(ss1,s1);//"abbaabbbaabbbb"
StrAssign(ss2,s2);//"aabbbaabb"
printf("主串为:");
printstr(ss1);
printf("模式串为:");
printstr(ss2);
index_next(ss2);
index_nextval(ss2);
int result;
result=index_KMP(ss1,ss2,1);
if(result) printf("在主串第%d处匹配成功\n",result);
else printf("匹配失败\n");
}