最近温习了一下KMP算法。现在谈谈我对KMP算法的理解。
KMP算法目的:尽量快的解决单字符串匹配的问题。
一、问题:
主字符串: ababcababababcab
模式串: abababab
判断在主串中是否存在模式串,如果存在,在哪个位置。
二、解决
解决办法很多,单谈KMP算法的next获取方法。
next数组的目标:在匹配失败的时候,将模式串的下标位置尽量的少的前移,使匹配速度最快。
进一步说就是:在模式串的每一个位置进行切割(不包括当前位置),前面一段字符串中,找出一个后缀(即不包括第一个字符),使得完全匹配自身的前缀。
可能有点绕口,看图!
1. 为了更直观起见,我将模式串进行放大并影分身!你看到的是两个模式串,但其实是一个(后面不再提醒)。
三、编程
1. 在编程的时候,很多地方都会把next的第一个位置设置为-1。即表示主串下标后移一位,重新开始匹配模式串。
其实根据当前下标为0,且匹配失败这种情况,将主串下标后移也是可行的,不过书写起来比较麻烦而已。
注意:可能你会注意到,在没有优化的KMP算法中,总是有next[0] = -1; next[1] = 0;
原因是什么?看前面二中的图部分。多想想就知道了!
next获取的c代码,来源: http://blog.csdn.net/sowhat_ah/article/details/42168727
int get_next(const char *pstr, int *next){
int i = 0;
int k = -1;
int plen = strlen(pstr);
int mlen = plen - 1;
next[i] = k;
while(i < mlen)
{
if((-1 == k) || (pstr[i] == pstr[k]))
{
next[++ i] = ++ k;
}
else
{
k = next[k];
}
}
return plen;
}
2. 关于KMP的优化。就是优化next数组。
优化的原因:
a. 首先应该理解,next值的意思是将模式串的下标移到该位置,再与主串进行比较。
b. 那你想啊,如果模式串下标移动之后的位置,和当前下标指的字符是同一个字符,那么匹配肯定还是失败的。还要继续前移!
例如在位置4处匹配失败,根据next[4]=2;跳转!
第1步:按照扇面1中的代码,建立next数组。
第2步:遍历next数组,如果跳转到的位置和当前的位置是相同的字符,则把当前的next值赋为跳转到的位置的next值。如下图。
(只遍历到3的位置,后面依次类推。)
最后奉上优化的代码(将第1步和第2步合并)。
//zh for zhuhong
int get_next_zh(const char *pstr, int *next)
{
int i = 0;
int k = -1;
int plen = strlen(pstr);
int mlen = plen - 1;
next[i] = k;
while(i < mlen)
{
if((-1 == k) || (pstr[i] == pstr[k]))
{
next[++ i] = ++ k;
// here is the modification
if(pstr[i] == pstr[k])
{
next[i] = next[k];
}
// end the modification
}
else
{
k = next[k];
}
}
return plen;
}