KMP算法是一种字符串匹配的高效算法,简单的匹配算法(BF算法)时间复杂度为O(m*n),KMP算法的时间复杂度为O(m+n)。
例如:当主串S为ABCDEFGH,匹配串T为ABCDX。(其中数组下标0来存放字符串长度)
其中E与X不匹配,按照BF算法,应该S串右移一位,T串重新回到开始即:
但是因为j[1]!=j[2],i[1]=j[1],i[2]=j[2],所以不用比较就可以知道j[1]肯定不等于i[2]。按照KMP算法直接将j[1]与i[5]比较即:
当主串是AAABD,匹配串是AAB。
可以看出i[3]与j[3]失配,根据KMP算法,因为j[1]=j[2],i[2]=j[2],所以i[2]=j[1],因此要这样匹配
当主串是AABAAC,匹配串是AAC,即
按照原理应该这样匹配
我们可以创建一个next数组,指导模式匹配串下一步改用第几号元素进行匹配。如上述第一个例子:
如果是例子二:
可以得出一个结论:KMP算法问题是由模式串决定,而不是主串决定。
如何来求next数组呢?
首先应该明白前缀和后缀的概念。"前缀"指除了最后一个字符以外,一个字符串的全部头部组合;"后缀"指除了第一个字符以外,一个字符串的全部尾部组合。
例如字符串ABABAAB。
”A“的前缀和后缀都为空集,共有元素的长度为0,next数组填1
“AB”的前缀为A后缀为B,共有元素的长度为0,next数组填1
“ABA”的前缀为A,AB,后缀为BA,A,共有元素长度为1,next数组填2
“ABAB”的前缀为A,AB,ABA,后缀为B,AB,BAB,共有元素为长度为2,next数组填3
“ABABA”的前缀为A,AB,ABA,ABAB,后缀为A,BA,ABA,BABA,共有元素长度为3,next数组填4
“ABABAA”的前缀为A,AB,ABA,ABAB,ABABA,后缀为A,AA,BAA,ABAA,BABAA,共有元素长度为1,next数组填2.
如图所示:
求next数组源码如下:
void get_next( String T, int *next )
{
//i后缀,j前缀
int j = 0;
int i = 1;
next[1] = 0;
while( i < T[0] ) //当后缀等于串的长度,匹配完成
{
if( 0 == j || T[i] == T[j] )
{
i++;
j++;
if( T[i] != T[j] )
{
next[i] = j;
}
else
{
next[i] = next[j];
}
}
else
{
j = next[j]; //若不相等回溯到上一次匹配的位置
}
}
}
KMP算法源码:
// 返回子串T在主串S第pos个字符之后的位置
// 若不存在,则返回-1
int Index_KMP( String S, String T, int pos )
{
int i = pos;
int j = 1;
int next[255];
get_next( T, next );
while( i <= S[0] && j <= T[0] )
{
if( 0 == j || S[i] == T[j] )
{
i++;
j++;
}
else
{
j = next[j]; //若不匹配
}
}
if( j > T[0] )
{
return i - T[0];
}
else
{
return -1;
}
}