KMP模式匹配算法
#include<stdio.h>
#define MAXSIZE 255
typedef unsigned char SString[MAXSIZE+1];//0号单元存放串的长度
void get_next(SString s,int next[])
{
int i,j;
next[1]=0;//特殊情况i=1;
i=1;
j=0;
while(i<s[0])
{
if(j==0)
{
++i;//先自加1
++j;
next[i]=j;
}
else if(s[i]==s[j])
{
++i;
++j;
next[i]=j;
}
else
{
j=next[j];//不相等j则跳到next[j]的位置
}
printf("i=%d,j=%d,next[%d]=%d\n",i,j,i,next[i]);
}
}
int KMP(SString s,SString t,int pos)
{
int i=pos;
int j=1;
int next[255];
get_next(t,next);
while(i<=s[0]&&j<=t[0])
{
if(j==0)
{
++i;
++j;
}
else if(s[i]==t[j])
{
printf("i=%d,j=%d,%c=%c\n",i,j,s[i],t[j]);
++i;
++j;
}
else
{
j=next[j];
}
}
if(j>t[0])
return i-t[0];//匹配成功则返回字串的第一个位置
else
return 0;
}
int main()
{
char s[]="abcabab";
char t[]="caba";
int i;
SString str,ttr;
for( i=0;s[i]!='\0';i++)
str[i+1]=s[i];
str[0]=i;
for( i=0;t[i]!='\0';i++)
ttr[i+1]=t[i];
ttr[0]=i;
printf("位置:%d\n",KMP(str,ttr,1));
return 0;
}
KMP算法是为了解决基本的模式匹配的一个缺点,那就是回溯。基本的的模式匹配就是一个两个循环语句,i用来作为主串的下标,j用来作为字串的下标,如果a[i]=b[j],那么i和j都向后移动一个位置,如果a[i]和b[j]不相等,则令i=i-j+1,j=1,这样一来明显的增加了比较的次数,进行了重复遍历。
KMP算法就是为了解决重复遍历的问题,当比较的字符一样时,和基本匹配算法一样,i和j同时往后移动一个位置;不等时,不是像基本匹配算法那样回溯,此时我们的i不变,但是j不是回退到1的位置,而是回退到next[j]的位置。
下面来讨论一下next数组的数值求法:
1.next[1]=0
2.对于j>1,next[j]的求法如下:
取出字符串a[1]—a[j-1],确定这个取出的字符串的前缀和后缀相同部分的字符个数,然后加一,比如aba,个数就为2;aaa
个数就为3
3.不在1和2情况中的a[j]取1
注意:a[2]属于情况3
改进后的KMP
KMP算法虽然有效地解决了回溯重复遍历的问题,但是也存在这样的一个问题,比如主串为aaaabcde,字串为aaaac,next数组为012345,当b与c不等时,会用b与第4个a再比较,结果发现不等。然后再用第3个a与b比较…
其实从第4个a判断之后就没有必要再判断了,因为都是a,既然第4个a不与b匹配,那么前几个a与b必然也不匹配。
所以我们对next数组进行改良,改良后的数组记为nextval
nextval的求法如下:依然要求next,在求出next的同时,进行如下判断,如果在求第j个位置的next时,a[[next[j]]=a[j],那么就令nextval[j]=nextval[next[j]],如果不等,那么nextval[j]还是等于next[j]
#include<stdio.h>
#define MAXSIZE 255
typedef unsigned char SString[MAXSIZE+1];//0号单元存放串的长度
void get_nextval(SString s,int nextval[])
{
int i,j;
nextval[1]=0;//特殊情况i=1;
i=1;
j=0;
while(i<s[0])
{
if(j==0||s[i]==s[j])
{
++i;//先自加1
++j;
if(s[i]!=s[j])
nextval[i]=j;
else
nextval[i]=nextval[j];
}
else
{
j=nextval[j];//不相等j则跳到next[j]的位置
}
printf("i=%d,j=%d,nextval[%d]=%d\n",i,j,i,nextval[i]);
}
}
int KMP(SString s,SString t,int pos)
{
int i=pos;
int j=1;
int nextval[255];
get_nextval(t,nextval);
while(i<=s[0]&&j<=t[0])
{
if(j==0)
{
++i;
++j;
}
else if(s[i]==t[j])
{
printf("i=%d,j=%d,%c=%c\n",i,j,s[i],t[j]);
++i;
++j;
}
else
{
j=nextval[j];
}
}
if(j>t[0])
return i-t[0];//匹配成功则返回字串的第一个位置
else
return 0;
}
int main()
{
char s[]="abcababaaaba";
char t[]="ababaaaba";
int i;
SString str,ttr;
for( i=0;s[i]!='\0';i++)
str[i+1]=s[i];
str[0]=i;
for( i=0;t[i]!='\0';i++)
ttr[i+1]=t[i];
ttr[0]=i;
printf("位置:%d\n",KMP(str,ttr,1));
return 0;
}