在使用BF算法进行模式匹配时,效率较低,时间复杂度到过了o((n+m)m)后来出现的KMP算法相对于BF算法在效率上有了较大的提高o(m+n)。BF算法效率低是因为每次它的模式串指针都要回溯到开头的位置。如果避免每次都回到开头,那么效率就会提高,这就是KMP的思想之一。
如有 目标串:T(t0t1t2t3.....tk......tm-1)
模式串: s(s0s1s2s3...sk.....sk-1)
若模式和目标串在k处出现了不同,则前可以把T改成(s0s1s2s3...sk-1,tk,tk+1.....tm-1)由此看来,每一次比较就没必要从头开始了,在模式串中存在着某种对应关系来确定指针的回溯位置。这种关系我们用“失配函数”来表示。失配函数用一个数组next[j]表示(j模式串的字符个数)next[0]规定为-1。其它的可以用下面的方法确定。
把j当作失配函数的定义域 其取值范围为 0=<j<s.size();
k为失配函数的值域,其取值为在0=<k<j中使得p0p1..pk=pk-jpk-j+1...pj成立的K的最大值。
如:s="caatcat"
当j=1时 k=0, s[0]!=s[1] next[1]=-1;
当j=2,k=0,1, s0s1!=s1s2;s0!=s2,next[2]=-1;
j=3,k=0,1,2; s0!=s3,s0s1!=s2s3,s0s1s2!=s1s2s3;s[3]=-1;
j=4,k=0,1,2,3 s0=s4,s0s1!=s3s4,s0s1s2!=s2s3s4;s0s1s2s2!=s1s2s3s4;next[4]=0;
j=5,k=0,1,2,3,4 ,s0s1=s4s5;next[5]=1;
j=6,k=0,1,2,3,4,5,next[6]=0;
然后是比较过程:
设T是目标串,S是模式串,并设指针i,j分别指向T,S要比较的字符,开始令i,j都等于0,比较t(i)和S(j)如果相等,则i和j都加1,如果不等,则j回到next[j]。i不变。再用t(i)和s(next[j])比较,如果相等,各回1,如果next[j]=-1则从T(i+1)和t0开始比较。
具体实现如下:
# include <iostream>
# include <string>
using namespace std;
//失配函数的实现
void nextval(string s,int next[])
{
int j=0,k=-1;
next[0]=-1;
while(j<s.size())
{
if(k==-1||s[j]==s[k])
{
j++;
k++;
if(s[j]!=s[k])
next[j]=k;
else
next[j]=next[k];
}
else
k=next[k];
}
}
int find(string s,string t)
{
int next[100],i=0,j=0;
nextval(t,next);
int lens=s.size();
int lent=t.size();
while(i<lens&&j<lent)
{
if(j==-1||s[i]==t[j])
{
i++;
j++;
}
else
{
j=next[j];
}
}
if(j>=t.size())
{
return (i-t.size());
}
else
return -1;
}
int main()
{
string s("abcabaa");
string t("abcaabbabcabaacbacba");
int x=find(t,s);
if(x==-1)
cout<<"查找失败"<<endl;
else
cout<<"从第"<<x<<"位开始匹配成功"<<endl;
return 0;
}