BF算法、KMP算法、改进的KMP算法,以上几种算法都是模式匹配算法,即找模式串在主串中第一次出现的位置。
BF算法:
如上图所示 主串:”abcaba” 子串:”aba”
思路:从主串第一个开始和子串第一个匹配,如果相等则两个串都向后走一步,继续匹配。如果不相等则主串从i-j+1开始、子串从头开始重新匹配。可以看到每当失配时,主串的i需要从开始匹配的下一个字符开始,重新和子串开始匹配。即每次失配了就执行 i=i-j+1,j=0操作。整个时间复杂度O(n*m)。n表示主串长度、m表示子串长度。
代码:
int BF(const char* s,const char* sub,int pos)
{
int i=pos;
int j=0;
int lens=strlen(s);
int lensub=strlen(sub);
while(i<lens && j<lensub)
{
if(s[i]==sub[j])
{
i++;
j++;
}
else
{
i=i-j+1;
j=0;
}
}
if(j>=lensub)
{
return i-j;
}
return -1;
}
KMP算法的实现:
思路:主串的i不回退,子串的j也只回退到指定的位置,而不用每次都会退到0。因为有些回退是没有必要的。而具体j回退到哪个位置,就需要我们写next数组了。
next数组存在的意义:不用将j回退到0,而是回退到next数组的值
代码:
void GetNext(int* next,const char* sub)
{
int lensub=strlen(sub);
next[0]=-1;
next[1]=0;
int i=2;//开始从第3位,下标为2的开始,是匹配该位之前的
int k=0;
while(i<lensub)
{
if((k==-1) || sub[k]==sub[i-1])//如果两个相等
{
next[i++]=++k;//i++表示当前i的next得出来后,之后计算i的下一个next。++k是相等情况下k+1;
}
else
{
k=next[k];//k处失配的
}
}
}
int KMP(const char* s,const char* sub,int pos)
{
int i=pos;
int j=0;
int lens=strlen(s);
int lensub=strlen(sub);
int* next=(int*)malloc(sizeof(int)*lensub);
GetNext(next,sub);
while(i<lens && j<lensub)
{
if(j==-1 || s[i]==sub[j])
{
i++;
j++;
}
else
{
j=next[j];//next[j]保存的是j位置失配时应该回退的位置
}
}
free(next);
if(j>=lensub)
{
return i-j;
}
return -1;
}
改进的KMP算法:
nextval代码:
void GetNextVal(int* nextval,const char* sub)
{
int lensub=strlen(sub);
nextval[0]=-1;
int i=0;
int k=-1;
while(i<lensub)
{
if((k==-1) || sub[k]==sub[i])
{
++i;
++k;
if(sub[k]==sub[i])
{
nextval[i]=nextval[k];
}
else if(i<lensub)//可能当前i<lensub,因为 k==-1而执行了++i操作,导致后面nextval[i]越界
{
nextval[i]=k;
}
}
else
{
k=nextval[k];//k处失配的
}
}
}
下面我们来整体测试一些代码
int main()
{
const char* s="abcaabbcabcaabdab";
const char* sub="abbc";
cout<<BF(s,sub,0)<<endl;
cout<<KMP(s,sub,0)<<endl;
return 0;
}
运行结果:
为查看next数组和nextval数组添加一个打印函数
void Print(int *arr,int len)
{
for(int i=0;i<len;i++)
{
cout<<arr[i]<<" ";
}
cout<<endl;
}
现在来看一下两个数组的结果:
int main()
{
const char* s="abcaabbcabcaabdab";
const char* sub="aaaaaaaab";
int lensub=strlen(sub);
int lens=strlen(s);
int* next=(int*)malloc(sizeof(int)*lens);
int* nextval=(int*)malloc(sizeof(int)*lens);
GetNext(next,sub);
GetNextVal(nextval,sub);
Print(next,lensub);
Print(nextval,lensub);
GetNext(next,s);
GetNextVal(nextval,s);
Print(next,lens);
Print(nextval,lens);
return 0;
}
结果:
和我们想的一样。但是有些书上起始是从0开始而不是从-1开始,这样的话我们还是按这种方法计算next数组,然后将数组的每个值都加1就好了。