KMP算法是一种高效的模式匹配算法。
与BF算法的比较
在普通的匹配算法BF中,目标串和模式串的索引值都需要重新回到起点再次进行匹配,所以该算法的时间复杂度较高,达到O(m*n)。KMP算法的核心思想是寻找模式串本身的特征,在此基础上达到目标串不回溯,模式串有规律回溯的目的,以减少回溯比较的次数,降低时间复杂度,达到O(m+n)。但是这是建立在提高空间复杂度的基础上的。
BF算法:时间复杂度O(m*n);空间复杂度O(1)
KMP算法:时间复杂度O(m+n);空间复杂度O(n)
KMP算法核心
KMP算法的核心是寻找模式串本身的规律。在该算法中表现为反映该规律的next数组。next数组的作用是在每次失配时,模式串根据next数组对应位置上的值回溯模式串索引的位置。next数组的求法也是KMP算法的精华所在。在本文的参考博文[1]和参考博文[2]中均给出了next数组的具体求法,但是遗憾的是在[1]中,并没有给出next数组的具体含义,而[2]中给出了具体的next定义。
next数组有如下定义:
(1) next[j]=-1 j=0
(2) next[j]=max ( k ): 0<k<j P[0...k-1]=P[j-k,j-1]
(3) next[j]=0 其他
该定义的意义就是next数组的首位均为-1。在其他位置上,该位置之前的子串中最长的相同前后缀长度(注释1)。当没有任何相同的前后缀的情况下,next值为0。
注释1:前后缀的含义,例如在a b c a b中,前缀和后缀均为"ab"。
next数组的计算方法
由next数组的定义可知,可以用一种递推的思想来求解next数组。
根据定义next[0] = -1
假设next[j] = k, 即P[0...k-1] == P[j-k,j-1]
(1)若P[j] == P[k],则有P[0..k]==P[j-k,j],很显然,next[j+1]=next[j]+1=k+1;
(2)若P[j] != P[k],则可以把其看做模式匹配的问题,即匹配失败的时候,k值如何移动,显然k=next[k]。
代码实现:
求next数组
void get_next(const char* ptrn,int* nextval)
{
int plen = strlen(ptrn);
int i = 0;
int k = -1;
nextval[i] = -1;
while(i < plen -1)
{
if (k == -1 || ptrn[i] == ptrn[k])
{
++i;
++k;
nextval[i] = k;
}
else
{
k = nextval[k];
}
}
}
KMP算法
int KMP_search(const char* src, const char* ptrn, const int *nextval )
{
int i=0;
int j=0;
int slen = strlen(src);
int plen = strlen(ptrn);
while(i<slen && j<plen)
{
if (j == -1 || src[i] == ptrn[j])
{
i++;
j++;
}
else
{
j = nextval[j];
}
}
if ( j >= plen)
{
return i - plen;
}
else
{
return -1;
}
}
参考博客[1]:http://blog.csdn.net/v_july_v/article/details/7041827
参考博客[2]:http://www.cnblogs.com/dolphin0520/archive/2011/08/24/2151846.html