【KMP】优质模板推荐+解读

本文详细介绍了KMP算法,包括如何构建next数组以实现快速匹配,以及匹配过程中回溯机制的工作原理。作者通过实例和代码解释了关键概念,适用于已了解KMP算法的读者进行扩展和复习。
摘要由CSDN通过智能技术生成

KMP – y神模板

引言:由于本人与4月20日前周五的一节数据结构课中偶然听到了老师讲kmp这个算法的概念并且发现老师讲的听不懂一点儿导致异常难受,于是花了两天左右自行弄懂了kmp算法的逻辑,并写下本文以便以后复习,并作留念。

声明:本文仅供对kmp理解的人扩展思路,不能教会不会kmp算法的人kmp的理解核心 Σ(っ °Д °;)っ

还不会kmp?:以下学习资料(教程链接)助你速通,

  1. 大神左程云细说kmp
  2. kmp简单讲解动画
  3. 民间讲解教程
  4. AcWing算法基础课–第二单元内容(需要付费)

题目练习

一、next数组快速生成

#include <iostream>

using namespace std;

const int N = 1e5+1, M = 1e6+1;
char s[M], p[N];
int n, m, ne[N];

int main() {
    cin >> n >> p+1 >> m >> s+1;
    // 创建next数组代码:
    for (int i = 2, j = 0; i <= n; i ++) {
        while (j && p[i] != p[j+1]) j = ne[j];
        if (p[i] == p[j+1]) j ++;
        ne[i] = j;
    }
    // kmp匹配代码:
   /*
   		暂时省略
   */
    return 0;
}

前置条件的解释

对于代码中的变量名,这里进行解释:

变量名含义
s主串
p模式串(需要在主串中被匹配并找到位置)
n模式串长度
m主串长度
i当前需要求的next数组下标位置
ji位置字符回溯后的位置

对于本模板需要注意三个小的细节:

  1. 输出输出的字符下标都是从1开始存储,0位置置空
  2. 每次比对时都是比对s串的i下标和p串的j+1位下标
  3. 回溯:也是kmp和暴力匹配的本质区别,在于匹配失败,instead of i向后移动,but j 向前移动我称之为"回溯",注意和递归那个回溯本质不是同一个东西。

如图所示:

在这里插入图片描述

对于模式串ababab在字符数组中的存储情况如上。

代码图示解读

for循环

对于代码:

for (int i = 2, j = 0; i <= n; i ++)

ij的默认位置如图所示:
在这里插入图片描述

解释:

细节对应的原理
i=2i=1位置的相等前后缀最大长度一定是0
next数组中默认都为0
所以i直接跳过第一位即可
j=0每一次都比较j+1i位置是否相等,
最开始也就是第一次迭代需要求i=2位置next数组的值,
那么就需要比较j=1i=2位置在模式串p中字符是否相等
i<=n这个简单,越过n就超过模式串p的长度了,同时也就表明next数组计算完毕。
for循环中后半部分代码(比较简单放在前面)
if (p[i] == p[j+1]) j ++;
ne[i] = j;

这段代码的含义:如果i位置字符和j+1位置字符相等,j后移一位,并且计算出了nexti位置的值。

如图:

在这里插入图片描述

for循环中第一句
while (j && p[i] != p[j+1]) j = ne[j];

如果不相等,使用ne(变量名冲突所以不适用next)数组回退。

二、kmp匹配部分

#include <iostream>

using namespace std;

const int N = 1e5+1, M = 1e6+1;
char s[M], p[N];
int n, m, ne[N];

int main() {
    cin >> n >> p+1 >> m >> s+1;
    // create nextArray.
    for (int i = 2, j = 0; i <= n; i ++) {
        while (j && p[i] != p[j+1]) j = ne[j];
        if (p[i] == p[j+1]) j ++;
        ne[i] = j;
    }
    // kmp.
    for (int i = 1, j = 0; i <= m; i ++) {
        while (j && s[i] != p[j+1]) j = ne[j];
        if (s[i] == p[j+1]) j ++;
        if (j == n) {
            cout << i - n << ' ';
            j = ne[j];
        }
    }
    return 0;
}

每次进行匹配,

  • 如果匹配成功j++

  • 如果匹配不成功,并且j还有向前回溯的可能,则回溯j = ne[j]

  • 如果已经匹配出一个结果,j == n,打印这个子串的初始位置,并且向前回溯

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值