KMP 算法之得到next的代码

最近温习了一下KMP算法。现在谈谈我对KMP算法的理解。

KMP算法目的:尽量快的解决单字符串匹配的问题。

一、问题:

主字符串:
ababcababababcab

模式串:
abababab

判断在主串中是否存在模式串,如果存在,在哪个位置。

二、解决

解决办法很多,单谈KMP算法的next获取方法。

next数组的目标:在匹配失败的时候,将模式串的下标位置尽量的少的前移,使匹配速度最快。

进一步说就是:在模式串的每一个位置进行切割(不包括当前位置),前面一段字符串中,找出一个后缀(即不包括第一个字符),使得完全匹配自身的前缀。

可能有点绕口,看图!

1. 为了更直观起见,我将模式串进行放大并影分身!你看到的是两个模式串,但其实是一个(后面不再提醒)。

《KMP 算法之得到next的代码》

2. 那,这样我们得到一段字符串aba,它只存在一个后缀a,与其本身的前缀a匹配。如下图。

《KMP 算法之得到next的代码》 3. 则,这个位置的next值就为1。即next[3]=1;如下图。

《KMP 算法之得到next的代码》 4. 同理,如果我们在5位置切割,如下图,得到next[5] = 3;如下图。

《KMP 算法之得到next的代码》 5. 依次类推,所有的位置next值都可以得到,如下图。

《KMP 算法之得到next的代码》 那么next值是什么,就是在当前位置匹配失败的时候,下标应该改变到的位置,继续进行匹配。

三、编程

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;跳转!

《KMP 算法之得到next的代码》 匹配之后跳转到位置2的时候,还是a!!!肯定匹配不成功了!所以还要继续往前跳!!!如下图。

《KMP 算法之得到next的代码》 如果在建立next的时候,提前把这个“我再跳”的操作已经做好,一步跳到最前面!岂不是更好!!!如下图:

《KMP 算法之得到next的代码》 了解到这里,其实可以把next数组分两步走,

第1步:按照扇面1中的代码,建立next数组。

第2步:遍历next数组,如果跳转到的位置和当前的位置是相同的字符,则把当前的next值赋为跳转到的位置的next值。如下图。

(只遍历到3的位置,后面依次类推。)

《KMP 算法之得到next的代码》 最终得到的next如下图,阴影部分均为第2步的所述的原因。

《KMP 算法之得到next的代码》

最后奉上优化的代码(将第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;
}

    原文作者:KMP算法
    原文地址: https://blog.csdn.net/henuyx/article/details/44653799
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞