KMP算法--计算Next数组

这篇文章写得是真烂,我都看不太懂了,回头我抽空改改,不过下面的代码是正确的


*  next[n]代表字符串前n个字符组成的子串的长度相同且相等的最长前后缀串的长度
*  就比如 "abab"中前缀(以首字符开头不包括尾字符的子串)有: "a"、"ab","aba"; 后缀(概念相反):"b","ab","bab"
*  那么"abab"中很明显长度相同且相等的最长前后缀串是 "ab", 那么 next[4] = 2
*
*  最简单求字符串的最长相等前后缀的方法: 从 index = len(str) - 1(先看看最长串是否一样)开始,
*  比较str[0]~str[index] 和 str[len - index] ~ str[len - 1], 如果两个字符串相等则直接退出返回index+1,否则index--,直到index小于0
*
*  虽然上述方法简单,但太低效了,如果在计算next[k]时我们能利用之前算的next[0]~next[k-1],就好了
*  推导: 如果 next[q] = k, 会有这个:(像两个板子尽量合在一起使重合部分一模一样但不能完全覆盖)
*                                  str[0] ~ str[k-1] ~ str[q-1]
*                    str[0] ~ str~[len-k] ~ str[q-1]
*
*   即 str[0] ~ str[k-1] 和 str~[q-k] ~ str[q-1] 相等
*  1、 此时计算 next[q+1], 如果 str[k] = str[q], 那么 next[q+1] = next[q] + 1: 没毛病
*
*                                  str[0] ~ str[k-1]str[k] ~ str[q-1]str[q]
*                    str[0] ~   str~[q-k] ~ str[q-1]str[q]
*
*   那你咋知道next[q+1]不能>K+1呢,确实,如果是一般的情况下,重合长度变长,有这个可能
*   假如存在 next[q + 1] = j > k + 1 :
*                          str[0] ~ str[j-2]str[j-1] ~ str[q]
*                str[0] ~ str[q-j]~ str[q-1]str[q]
*   此时把上下两个str[q]都挡住,此时很明显: next[q] = j-1 > k, 所以不符, next[q+1] <= k+1
*
*  2、 如果 str[k] != str[q],那么只能在 str[0]~str[k-1]中尽量找某个长度为j + 1的前缀串str[0]~str[j]
*   使得其和 str[q-k+j]~str[q-1](即在str[q-k]~str[q-1]中长度相等的后缀串)相等,并满足str[j+1] = str[q],此时 next[q+1] = j + 1
*
*                        str[0]~str[j]str[j+1]~str[k-1]str[k] ~ str[q-1]str[q]
* str[0] ~ str~[q-k]~str[q-k+j]~str[q-1]str[q]
*
*   当然我们不需要一个个试j值,其实我们已经有了j的值的最佳候选,仔细看看,我们已经证明了str[0] ~str[k-1] = str[q-k]~str[q-1]
*   如果要找前者的前缀串与后者的后缀串最大相等长度,再者两者相等,那就不是找str[0] ~str[k-1]长度相同且相等的最长前后缀串的长度
*   这不就是next[k]的定义吗,此时 k < q,所以 next[k] 已知
*   所以我们只需要令 k = next[k], 此时 str[0]~str[k-1]和 str[q]之前的k个字符必然相等,如果此时 str[k] = str[q], 那么 next[q + 1] = k + 1, 否则继续上述步骤找内部的最长串,k=next[k],直到k = 1,此时若还不满足, next[q+1] = 0,没辙了
*

代码如下:

#include <cstring>
#include <iostream>

using namespace std;

//"abab",next[5],len = 4 --> next[5]{-1,0,0,1,2}
//next[0]=-1,next[n]代表前n个字符的最长相等前后串长度
void calculateNext(const char *str, int *next, int len) {
    next[0] = -1; //递归结束位, k < 1不就是 k = next[k] <= -1
    next[1] = 0;
    int k = next[1]; //k是上一个next的结果
    for (int i = 2; i <= len; ++i) {
        while (str[k] != str[i - 1] && k > -1) {
            k = next[k]; //回溯
        }
        if (k > -1 && str[k] == str[i - 1]) { //找到了
            k++;
        } else {
            k = 0; //还没找到
        }
        next[i] = k;
    }
}

int main() {
    char str[] = "abab";
    int len = strlen(str);
    int next[len];
    calculateNext(str,next,len);
    for(int i = 0;i< len; i++)
        cout << next[i] << ",";
    return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值