KMP算法-简单匹配算法的改进
KMP算法是D.E.Knuth、J.H.Morris和V.R.Pratt共同提出的,简称KMP算法。
该算法较BF算法有较大改进,主要是消除了主串指针的回溯,从而使算法效率有了某种程度的提高。
在介绍KMP算法之前,首先介绍一个概念-真子串:、
所谓真子串是指模式串t存在某个k(0<k<j),使得"t0t1…tk " = " tj-ktj-k+1…tj "成立。
例如,t= "abab",
即t0t1=t2t3
也就是说, “ab”是真子串。
真子串就是模式串中隐藏的信息,利用它来提高模式匹配的效率。
模式串t=“abcac”,用next数组存放这些“部分匹配”信息 。
j | 0 | 1 | 2 | 3 | 4 |
t[j] | a | b | c | a | c |
next[j] | -1 | 0 | 0 | 0 | 1 |
归纳起来,定义next[j]函数如下:
max{k|0<k<j,且“t0t1…tk-1”=“tj-ktj-k+1…tj-1” }
当此集合非空时
-1 当j=0时
0 其他情况
t=“abab”对应的next数组如下:
j | 0 | 1 | 2 | 3 |
t[j] | a | b | a | b |
next[j] | -1 | 0 | 0 | 1 |
设主串s的长度为n,子串t长度为m。
在KMP算法中求next数组的时间复杂度为O(m),在后面的匹配中因主串s的下标不减即不回溯,比较次数可记为n,所以KMP算法总的时间复杂度为O(n+m)。
匹配示意图如下:
KMP算法的实现如下:
#include "sqstring.cpp"
void GetNext(SqString t,int next[]) /*由模式串t求出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]) /*k为-1或比较的字符相等时*/
{
j++;k++;
next[j]=k;
}
else k=next[k];
}
}
int KMPIndex(SqString s,SqString t) /*KMP算法*/
{
int next[MaxSize],i=0,j=0;
GetNext(t,next);
while (i<s.length && j<t.length)
{
if (j==-1 || s.data[i]==t.data[j])
{
i++;j++; /*i,j各增1*/
}
else j=next[j]; /*i不变,j后退*/
}
if (j>=t.length)
return(i-t.length); /*返回匹配模式串的首字符下标*/
else
return(-1); /*返回不匹配标志*/
}
void main()
{
SqString s,t;
StrAssign(s,"ababcabcacbab");
StrAssign(t,"abcac");
printf("s:");DispStr(s);
printf("t:");DispStr(t);
printf("位置:%d\n",KMPIndex(s,t));
}