KMP算法用于串的模式匹配,主串S,子串T(也叫模式串),模式匹配意思是从S中找出跟T一样的子串,就是说判断S是否包含T,时间复杂度O(m+n),实现这个算法关键有两步,第一步,求next数组,第二步KMP主算法
如何理解next数组,**next[j]表示当子串T中第j个字符与主串中相应字符“失配”时,在子串T中需要重新和主串S中该字符进行比较的字符的位置。**例子:
求:主串S=“ababcabcacbab”,子串T=“abcac”KMP算法匹配过程
假如说已经求出来next数组(最后再研究next数组怎么求)如下:
下面进行KMP匹配,i与j分别标记主串与子串位置,且都从1开始
如上图,我们从主串S第1位a开始对应匹配,匹配时候发现i等于3,j等于3时候不匹配,怎么办呢?一般我们都会从主串S第2位b开始再和abcac逐个匹配,但有了next数组就不必那样做了,此时i不必退到第2位b处,应保持不动,主串不动,子串那必须行动行动,因为不匹配位置j=3,根据next数组说法,next[j]表示当子串T中第j个字符与主串中相应字符“失配”时,在子串T中需要重新和主串S中该字符进行比较的字符的位置,此时next[3]=1,故j移动到T的第1位,即用T第1位与Si所指的第3位继续匹配比较,如下:
可以看出,再次匹配时,i=7,j=5时候有不匹配了,此时再处理处理,保持i=7不动,j变为:j=next[j],此时j等于2,如下:
接下来按照以上步骤一步步来(能看出来此时其实已经匹配好了)
所以KMP匹配代码就容易写了(纯C):
int KMP(char S[], char T[], int pos)
{
i=pos; j=1;//post表示从主串什么位置开始,一般情况下都是从1开始的
while(i<=strlen(S)-1&&j<=strlen(S)-1)//strlen(S)-1是实际长度,S[0]不是真正字符内容
{
if (j==0||S[i]==T[j])//匹配到了的话继续往前走直到遇到不匹配的或匹配成功了
{
i++;
j++;
}
else
{
j=next[j];//当子串T中第j个字符与主串中相应字符“失配”时,在子串T中需要重新和主串S中该字符进行比较的字符的位置
}
}
if (j>=strlen(S))
return i-T[0];//匹配位置
else
return 0;
}
还有一个遗留问题:如何求next
观察此表,发现
next[j]表示前j-1个字符前后缀失配位置
即next[j]表示前j-1个字符前后缀匹配数目+1,基于此:
void get_next(char T, char next[ ])
{
i=1; next[1]=0; j=0;
while(i<T[0])
{
if (j==0||T[i]==T[j])
i++;
j++;
next[i]=j;
else
j=next[j];
}
}