KMP算法中next函数的实现

鉴于有很多人都在自己的博文中介绍了KMP的思想和主体函数,我就不赘述之了。这里我只为自己学习KMP算法过程中遇到的难题做一下笔记,是关于算法中next数组的求解的,方便自己以后翻阅,也方便有相同需求的人浏览

建议大家在浏览的时候,自己可以在纸上画着图,更容易理解

  • 第一个难点在于:理解next数组

假设我们的模式串为 P

我们知道,KMP算法中,next的定义是:

next [ j ] = -1 ,若 j = 0 
next [ j ] = max [ k ] ,其中 P[ 0 ... k-1 ] = P[ j-k ... j-1 ]
next [ j ] = 0,其他

这定义是什么意思呢?

先来分析一下 next [ j ] 是什么意思,首先 j 表示在匹配过程中,P[ j ] 和主串S的 S[ i ] 不相等,既然不相等,根据我们的KMP算法,我们要找到P中的某一个位置 k ,回溯我们的P串的指针到 k 处。

而 k 的含义有是什么呢?它指代P中的一个位置,满足从P的头连续到位置 k - 1 的这个子串,我们设为 p1,要和 j 之前的某个连续子串p2相等。注意,p2一定要紧挨在 P[ j ] 后面的。

k 的意义在于,只要我们知道了一个k ,那就表示我们可以不重复去匹配 P[ 0 ... k-1 ](想想看BF算法,它每次都要从头开始匹配),而可以直接从k开始匹配主串中的S[ i ]了。

而我们当然希望k越大越好啦,因为这样我们就能够匹配更少的子串

所以,这个next[ j ] 的求解,就是对应于每个 j ,都求解它的 k。


  • 明白了next的含义后,第二个难点就是:怎么求解next数组
我们这么给出一个前提: 假设对于一个 j ,我们已经知道 next [ j ] = k 了,也就是如果在P 和 S 在 j 处发生不匹配时,因为P[ 0 ... k-1 ] 我们都已经知道匹配好了,就可以直接回溯到 k 处,直接从k 开始继续匹配。那么,怎么求 next [ j + 1 ] 呢?有两种情况
  1.  P[ j ] = P [ k ],很显然,这说明我们在 j + 1 处发生不匹配时,P[ 0 ... k ] = P[ j + 1  - k ... j ] (注意我们上面的前提),所以 next [ j + 1 ]  = next [ j ] + 1 = k + 1 
  2.  P[ j ] != P[ k ] ,这个情况比较复杂,但是只要细细思考,也是可以想通的:我们必须移动k,而这个移动操作是 k = next[ k ],然后通过循环来解决next[ j + 1 ]
第二种情况是什么意思呢?

回顾我们的思路,我们要找的 k 要满足,前k个字符的子串要和位置 j 前的k个字符的子串相等

因为P[ j ] != P[ k ] ,所以我们没办法直接使用 k 了,只能从P的头开始找另一个基准 k ,满足上面的条件 ” 前k个字符的子串要和位置 j 前的k个字符的子串相等 “ 。

难道我们真的要从P的头开始一个个匹配吗?不!我们要利用我们的前提条件!至少我们要跳过一些字符!

大家想象 next [ k ] 的含义是什么?对 k 找到一个位置 x ,也满足从0 到 x 的子串 要和 k 前长度为 x 的子串相等,这样我们不就可以跳过 x 个字符了?有了这 x 个字符的基础,我们只要再找到 P [ j ] = P[ x ] 就行了。为什么?因为我们现在在求 next [ j + 1 ] 啊!如果 找到了x后发现不满足P [ j ] = P[ x ]怎么办?那就重复操作,再找 y = next [ x ] ,直到找到为止,这就是为什么我们要 k = next [ k ] 然后递归了


实现的代码如下:

void getNext(char *p, int *next)
{
    int i = 0, j = 0;
    int k = -1;
    next[0] = k   //根据next数组的定义
    while(j < strlen(p) - 1)
    {
        if( j == -1 || p[k] = p[j] )
        {
            j++;
            k++;
            next[j] = k;
        }
        else
        {
            k = next[k];
        }
    }
}



  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值