代码随想录-09|KMP算法

Before Writing


KMP算法主要应用在字符串的匹配上,其中的方法思路是较容易理解的,难点在于如何完成前缀表的构建代码(Next数组)。

本文主要剖析为什么可以通过简单几行代码就可以构建next前缀表,如果想快速了解原理直接跳转至对j = next[j-1]回溯的理解章节。

如果对字符串匹配不了解,可以先看这篇文章,对字符串匹配进行了大致描述,随后可以看代码随想录文章.

看完之后应该对如下概念有一定认知:

Q:字符串的结构和匹配
Q:前缀表
Q:前缀与后缀和最长公共前后缀
Q:BF算法及复杂度
Q:KMP算法思路
Q:next数组的概念

由于KMP算法中next数组的构建代码相对不是容易理解,这里着重介绍一下。


理论基础—next数组

NextPrefix都可以表示为前缀表的名字,习惯的将其命名为next数组。

在定义next数组的时候,定义下标的方式有两种,如下图所示:

  • 左闭右闭
    • 即next数组的下标i代表着主串的前i+1个字符中前后缀重复的字符数
    • 此时next数组的第一位被固定为== 0 ==
  • 左闭右开
    • 即next数组的下标i代表着主串的前i个字符中前后缀重复的字符数,当i=0时表示空
    • 此时next数组的第一位被固定为== -1 ==

在这里插入图片描述

  • 假设模版字符串为‘DABCDABD’,求得左闭右开next[]数组具体操作过程如下

    • 1.首先对‘DABCDABD’标注下标0-7
    • 2.从下标0开始依次向后遍历,当j=0的时候next[j]=-1
    • 3.next[j]的值为下标j的字符前面的子串的最大前后缀匹配字符数
      • 比如j=6时,前面子串为‘DABCDA’,‘DA’是最大匹配前缀
      • 比如j=5时,前面子串为‘DABCD’,‘D’是最大匹配前缀
        在这里插入图片描述
  • 注:为了方便记忆理解,本文使用左闭右闭的方式用于记录前缀表next[],其核心就是:

    • j遇到冲突时,找到冲突的前一位及j-1next[j-1]作为j的新位置
    • next[]数组主要是找到每个位置对应的前面子串的最大前后缀匹配数

next数组代码实现

void getNext(int* next, const string& s) {
    int j = -1;
    next[0] = j;
    for(int i = 1; i < s.size(); i++) { // 注意i从1开始
        while (j >= 0 && s[i] != s[j + 1]) { // 前后缀不相同了
            j = next[j]; // 向前回退
        }
        if (s[i] == s[j + 1]) { // 找到相同的前后缀
            j++;
        }
        next[i] = j; // 将j(前缀的长度)赋给next[i]
    }
}

  • 1.首先对next数组进行初始化赋值,将s模式串传入

    • void getNext(next, S),其中函数中有ij两个变量
      • j指向前缀末尾位置,代表着i之前(包括i)子串的最长相等前后缀的长度,初始化为0
      • i指向后缀末尾位置i初始化为1,因为后缀不包含第一个字符
      • for (int i = 1; i < s.size(); i++)
  • 2.处理前后缀不同的情况

    • 前后缀不同证明出现了s[i]后与s[j]不再匹配了,我们需要找到== 从i-1往前的前缀最大匹配的长度 ==
    • 由于前面我们已经得到了下标小于inext数组,为了利用前面的匹配结果,另j-1作为中介,查找next[j-1]即可找到== 从i-1往前的前缀最大匹配的长度 ==
    • 同时,为了在利用next[j-1]数组时发生越界,规定j>0
      • while (j > 0 && s[j] != s[i]) j = next[j-1];
  • 3.处理前后缀相同的情况

    • 如果s[i]==s[j],则继续对比下面的字符,并将i处对应的最大匹配长度记录到next数组中
      • j++; s[i] = j; i++;

j = next[j-1]回溯的理解

在这里插入图片描述


  • 以‘aabaaf’为示例,前后缀相同不同的处理情况和具体步骤
    在这里插入图片描述
    • 第一步:j=0, i=1, next初始化为0,此时s[j] == s[i]
      在这里插入图片描述
    • 第二步:j=1, i=2,此时s[j] != s[i],j回溯到next[j-1]直至s[j] == s[i]或者j = 0
      在这里插入图片描述
    • 第三步:j=0, i=3, s[j] == s[i]
      在这里插入图片描述
    • 第四步:j=1, i=4, s[i] == s[j]
      在这里插入图片描述
      *第五步:j=2, i=5, s[i]!=s[j],不断对j进行回溯
      在这里插入图片描述

Q

Q:字符串的结构和匹配
Q:前缀表
Q:前缀与后缀和最长公共前后缀
Q:BF算法及复杂度
Q:KMP算法思路
Q:next数组的概念

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值