KMP的next究竟是什么

KMP模式匹配算法的next数组到底是什么?

Ⅰ.为了搞清楚这个问题,首先先看个例子


基于了解了KMP算法,我们明白,当i和j对应的字符不相等时,我们不会对i进行回溯,而是调用了j=next[j],使j到达一个位置,但是这个位置究竟是哪里,就需要进行一定的计算,在计算之前,先让我们搞清楚这个next究竟是个啥?

Ⅱ.next值究竟是个啥?

对于上面的例子,i和j对应的字符不匹配,就需要j指向一个新的地方,那是哪呢?
先看画线的地方

首先 i和j前面的两个AB是匹配的,而模式串中前后都有AB,那么可以利用这一点,将j移动到c的位置
如图

我们利用了上面描述的信息,使得i不用进行回溯,而使j进行回溯,这样会减少回溯的次数

next是什么呢?我想大家心里是不是有一点点想法了? 规定字符串的第一个字符下标为0,next[j]的值,就是j之前的字符串中,前缀子串和后缀子串相等时,最长的长度。例如上边的例子,模式串S="ABCABD";则,S4在它之前的字符串是"ABCA",这个字符串的前缀子串和后缀子串相等的最长的子串是A,长度也就是1,所以next[4]=1,同样,对于上面的例子即S[5]不匹配时,"ABCAB",前后相等的最长的子串就是AB,所以,next[5]=2

说了这么多,大家应更明白了我的意思 我们当然可以使用统计前缀子串和后缀子串的方式来计算next数组的值,但是得不偿失,因为我们使用KMP的目的时为了降低暴力穷举所有子串的方法的时间复杂度,若使用统计子串的方式,时间复杂度可能会更高,所以用另一种方式来计算next数组  我们这里用一个k,来存储我们上述的最大的长度,"ABA"的那个最长的前后想的的是"A",则k=1,那么就可以这样理解k:k是j之前的前后相等的最长字串的长度,因为字符串从0开始,所以,k刚好指向了那个最长子串的后一个字符,划重点。。。所以,当k和j对应的字符相等时,也就是s[k]==s[j]的时候,j+1对应的那个最长子串的长度就是k+1,next[j+1]=k+1,也就是当j指向c时,k就会指向第二个A
接下来讨论s[k]!=s[j]的情况
如果理解了我上面说的话,对于j指向j,k指向D应该没有疑问了
s[K]和s[j]不相等了,那么,该怎么办呢,我们要让k重新指到一个地方,使得s[k]和s[j]相等,这样,就可以使用上面的公式s[j+1]=k+1来计算,不能简单地让k--,直到找到s[k]==s[j]位置(这个图片的例子不是很好),例如ABADABAAD,当j指向最后一个A时,k指向第一个D,如果简单的使用k--操作,k指向第二个A,也就是k为3,那么,next[j+1]=k+1,也就是当j指向最后一个D时,k指向了第一个D,很显然ABA不等于BAA,所以不能简单的用k--来找k的位置
这里其实又利用了KMP算法
将包括k的前半段拿下来方便我们分析,这是不是很像主串和模式串的j和k指向的字符不同,接下来我们需要让k=next[k]

接下来上一段各种书上都能见到的代码:

int *GetNext(char s[]){

    int j,k;
    int len;
    int *next;

    len = strlen(s);
    next = (int *)malloc(sizeof(int)*len);
    //默认next[0]为-1
    next[0]=k=-1;

    j=0;
    while(j<len){
        //k如果等于-1表明,连第一个字符和第j个不匹配
        //则将next[j]=0
        if(k==-1 || s[j]==s[k])
            next[++j]=++k;
        else
            k=next[k];
    }

    //关于优化
    for(j=1;j<len;++j){
        //如果j不匹配,j就会指到k的位置
        k=next[j];
        //如果s[j]和s[k]相等,则令next[j]=next[k]
        //这样会优化AAAAAA这样的子串
        if(s[j]==s[k])
            next[j]=next[k];
    }
    return next;
}

因为表达能力有限,我已经尽我最大努力解释了,最后代码这里解释我自己都不满意,有问题大家可以评论区回复我,我会回复的

猜你喜欢

转载自blog.csdn.net/qq2071114140/article/details/84455627