KMP算法是由D.E.Knuth、J.H.Morris和V.R.Pratt等人共同提出的,所以称为Knuth-Morris-Pratt算法,简称为KMP算法。KMP算法分析了模式串中隐藏的有利于模式匹配的信息。这种信息就是模式串中的“部分匹配”信息,或者,模式串中某个位置“前串等于后串”的最大长度信息。
模式串t的next[j]数组公式如下:
next[j]=⎧⎩⎨Max{k|0<k<j,′′t0t1⋯t′′k−1=′′tj−ktj−k+1⋯t′′j−1},−1,0,当此集合不为空时当j=0时其他情况
由此得到next[]数组的算法和KMP算法如下:
//求next[]
void GetNext(SqString t,int next[]){
int j,k;
j=0; k=-1; next[0]=-1;
while (j<t.length-1)
{
if (k==-1 || t.data[j]==t.data[k])
{
j++; k++;
next[j]=k;
}
else k=next[k];
}
}
//KMP算法:判断s与t是否匹配
int KMPIndex(SqString s,SqString t){
int next[MaxSize],i=0,j=0,v;
GetNext(t,next);
while (i<s.length && j<t.length)
{
if(j==-1 || j<t.length){
i++; j++;
}
else j=next[j];
}
if(j>=t.length) v=i-t.length;
else v=-1;
return v;
}
设主串
s
的长度为
上述算法,还可以改进一下,如下:
//求NextVal[]数组
void GetNextVal(SqString t,int nextVal[]){
int j=0,k=-1;
nextVal[0]=-1;
while(j<t.length){
if (k==-1 || t.data[j]==t.data[k])
{
j++; k++;
if(t.data[j]!=t.data[k]) nextVal[j]=k;
else nextVal[j]=nextVal[k];
}
else k=nextVal[k];
}
}
//修正后的KMP算法
int KMPIndex1(SqString s,SqString t){
int nextval[MaxSize],i=0,j=0,v;
GetNextVal(t,nextval);
while (i<s.length && j<t.length)
{
if (j==-1 || j<t.length)
{
i++; j++;
}
else j=nextval[j];
}
if(j>=t.length) v=i-t.length;
else v=-1;
return v;
}
与改进前的KMP算法一样,本算法的时间复杂度也为
O(n+m)
。虽然没有改变算法的数量级,但却减少了模式串与与主串的比较次数。
《数据结构(c语言版)》–严蔚敏,把next[j]定义如下:
next[j]=⎧⎩⎨Max{k|1<k<j,′′t1t2⋯t′′k−1=′′tj−k+1tj−k+2⋯t′′j−1},0,1,当此集合不为空时当j=1时其他情况
该公式与前面的公式是等价的,只不过next[j]定义的起点不同,前者j从0开始,next[0]=-1,next[1]=0,…; 后者j从1开始,next[1]=0,next[2]=1,…。如图(1)所示:
完整代码如下:
//Base.h
#include <stdio.h>
#define MaxSize 100
typedef struct{
char data[MaxSize];
int length;
}SqString;
//创建串
void StrAssign(SqString &str,char cstr[]){
int i;
for(i=0;cstr[i]!='\0';i++)
str.data[i]=cstr[i];
str.length=i;
}
//串复制
void StrCopy(SqString &s,SqString t){
int i;
for(i=0;i<t.length;i++)
s.data[i]=t.data[i];
s.length=t.length;
}
//判断串s与串t是否相同
int StrEqual(SqString s,SqString t){
int same=1,i;
if(s.length!=t.length)
same=0;
else{
for(i=0;i<s.length;i++)
if(s.data[i]!=t.data[i])
same=0;
}
return same;
}
//求串长
int StrLength(SqString s){
return s.length;
}
//两串比较
SqString Concat(SqString s,SqString t){
SqString str;
int i;
str.length=s.length+t.length;
for(i=0;i<s.length;i++)
str.data[i]=s.data[i];
for(i=0;i<t.length;i++)
str.data[s.length+i]=t.data[i];
return str;
}
//求串s中第i(1≤i≤n)个位置长度为j的子串
SqString SubStr(SqString s,int i,int j){
SqString str;
int k;
str.length=0;
if (i<=0 || i>s.length || j<0 ||
i+j-1 >s.length)
{
printf("参数不正确\n");
return str;
}
for(k=i-1;k<i+j-1;k++)
str.data[k-i+1]=s.data[k];
str.length=j;
return str;
}
//在串s的第i个位置插入串s2
SqString InsStr(SqString s1,int i,SqString s2){
int j;
SqString str;
str.length=0;
if(i<=0 || i>s1.length+1) //参数不正确时,返回空串
{
printf("参数不正确\n");
return s1;
}
for(j=0;j<i-1;j++) //将s1.data[0]~s1.data[i-2]复制到str
str.data[j]=s1.data[j];
for (j=0;j<s2.length;j++) //将s2.data[0]~s2.data[s2.length-1]复制到str
str.data[i+j-1]=s2.data[j];
for(j=i-1;j<s1.length;j++) //将s1.data[i-1]~s.data[s1.length-1]复制到str
str.data[s2.length+j]=s1.data[j];
str.length=s1.length+s2.length;
return str;
}
//删除串s中第i个位置长度为j的子串
SqString DelStr(SqString s,int i,int j){
int k;
SqString str;
str.length=0;
if (i<=0 || i>s.length || i+j>s.length+1)
{
printf("参数不正确\n");
return str;
}
for(k=0;k<i-1;k++)
str.data[k]=s.data[k];
for (k=i+j-1;k<s.length;k++)
str.data[k-j]=s.data[k];
str.length=s.length-j;
return str;
}
//在串s中,用串t替换在第i个位置长度为j的子串
SqString RepStr(SqString s,int i,int j,SqString t){
int k;
SqString str;
str.length=0;
if (i<=0 || i>s.length || i+j-1 >s.length)
{
printf("参数不正确\n");
return str;
}
for(k=0;k<i-1;k++)
str.data[k]=s.data[k];
for (k=0;k<t.length;k++)
str.data[i+k-1]=t.data[k];
for(k=i+j-1;k<s.length;k++)
str.data[t.length+k-j]=s.data[k];
str.length=s.length-j+t.length;
return str;
}
//输出串str
void DispStr(SqString str){
int i;
if (str.length>0)
{
for(i=0;i<str.length;i++)
printf("%c",str.data[i]);
printf("\n");
}
}
//主函数.cpp
#include "base.h"
#include <stdio.h>
/*----------------------KMP算法-------------------------*/
//求next[]
void GetNext(SqString t,int next[]){
int j,k;
j=0; k=-1; next[0]=-1;
while (j<t.length-1)
{
if (k==-1 || t.data[j]==t.data[k])
{
j++; k++;
next[j]=k;
}
else k=next[k];
}
}
//KMP算法:判断s与t是否匹配
int KMPIndex(SqString s,SqString t){
int next[MaxSize],i=0,j=0,v;
GetNext(t,next);
while (i<s.length && j<t.length)
{
if(j==-1 || j<t.length){
i++; j++;
}
else j=next[j];
}
if(j>=t.length) v=i-t.length;
else v=-1;
return v;
}
/*-------------------------改进的KMP算法---------------------*/
//求NextVal[]数组
void GetNextVal(SqString t,int nextVal[]){
int j=0,k=-1;
nextVal[0]=-1;
while(j<t.length){
if (k==-1 || t.data[j]==t.data[k])
{
j++; k++;
if(t.data[j]!=t.data[k]) nextVal[j]=k;
else nextVal[j]=nextVal[k];
}
else k=nextVal[k];
}
}
//修正后的KMP算法
int KMPIndex1(SqString s,SqString t){
int nextval[MaxSize],i=0,j=0,v;
GetNextVal(t,nextval);
while (i<s.length && j<t.length)
{
if (j==-1 || j<t.length)
{
i++; j++;
}
else j=nextval[j];
}
if(j>=t.length) v=i-t.length;
else v=-1;
return v;
}
void main()
{
SqString str,str2;
char s[20]="Helloworld";
char s2[10]="llowo";
StrAssign(str,s);
StrAssign(str2,s2);
//DispStr(str);
printf("主串为: %s\n",s);
printf("模式串为:%s\n",s2);
int isPipei=0;
int next[6];
// GetNext(str2,next);
// isPipei=KMPIndex(str,str2);
GetNextVal(str2,next);
isPipei=KMPIndex1(str,str2);
if (isPipei != -1)
{
printf("匹配成功!\n");
}
else{
printf("匹配失败!\n");
}
}
效果如下:
模式匹配(第一篇:
http://blog.csdn.net/sanqima/article/details/48863425