1. KMP简介

kmp算法,是一种线性字符串匹配(父子串为 root,子子串为 s),由 D.E.Knuth,J.H.Morris 和 V.R.Pratt 提出的,因此人们称它为KMP算法。

2. 暴力思路

举个样例吧。

下标 1 2 3 4 5 6 7 8 9
root a b c a a b c a b
s a b c a b

拿到这个串,我们让两个指针 \(l,r\) 分别指向 \(root\) 和 \(s\)。

共 \(len(root)\) 轮,对于第 \(i\) 轮,让 \(l\gets i,r\gets 1\),并执行按顺序执行以下操作:

  • 如果 \(r=len(s)\) 则找到 \(s\)。
  • 如果 \(r\ne len(s)\) 且 \(root_l=s_r\),则 \(l\gets l+1,r\gets r+1\),并重复该操作。
  • 如果 \(root_l\ne s_r\),则跳过该阶段。

就拿样例而言,对于第 \(1\) 轮,\(l=1,r=1\),发现 \(root_1=s_1\),则 \(l+1,r+1\)。当 \(l=5,r=5\) 时,\(root_5\ne s_5\),则进行第2轮,\(l=2,r=1\)......

你会发现,一旦中途不符合条件,已知的信息最会浪费。那有没有不浪费已知信息的算法呢?

答案是有的,那就是烤馍片算法。

3. KMP算法

3.1 过程

再来举个样例:

下标 1 2 3 4 5 6 7 8 9 10 11 12
root A B C D A B C D A B C E
s A B C D A B C E

我们匹配好前面八位的时候,发现第九位不对,我们就移动 \(s\),根据前面已知的,我们就可以像下面这样:

下标 1 2 3 4 5 6 7 8 9 10 11 12
root A B C D A B C D A B C E
s A B C D A B C E

直接向后移动四位,然后我们的 \(l\) 继续从第五位开始,直到匹配成功。

注意:我们都是判断 \(l\) 和 \(r\) 下一个位置是否为相同。

前缀串与后缀串

abcde

前缀串:a,ab,abc,abcd,abcde

后缀串:e,de,cde,bcde,abcde

其实,\(r\) 只是回跳到使得 \(root\) 前 \(l\) 段的后缀与 \(s\) 前 \(r\) 段的前缀相等的最长长度的位置(除了自己),有点长,大家可以分开理解。

例如,当 \(l=8,r=8\) 时,下一个位置不一样,则将 \(r\) 回退到 \(3\),因为 \(root\) 前 \(8\) 个中的倒数 \(3\) 个与 \(s\) 前 \(4\) 个中的前面 \(3\) 个一样都是 \(ABC\),所以回退到 \(3\)。

这里,我们为了预处理,简化一下,来发下一下性质。还是这个例子:

下标 1 2 3 4 5 6 7 8 9 10 11 12
root A B C D \(\red{A}\) \(\red{B}\) \(\red{C}\) D A B C E
s \(\green{A}\) \(\green{B}\) \(\green{C}\) D \(\purple{A}\) \(\purple{B}\) \(\purple{C}\) E

我们要用到的是红色和绿色的字母,这里就是最长公共前后缀。但是,大家想一想,紫色部分和红色部分是不是一模一样的呀,因为指针能到 \(7\),肯定是因为紫色部分和红色部分一样。

那这样子,紫色和红色一样,红色和绿色一样,那绿色就和紫色一样!这样,我们就只需要预处理 \(s\) 就可以了。

3.2 具体实现

到这里,我们可以写出伪代码(\(next\) 为 \(s\) 的最长公共前后缀):

if root[l] = s[r] then
l <- l + 1, r <- r + 1
else then
r <- next[r]

那怎么预处理 \(next\) 呢?再举一个例子:

下标 1 2 3 4 5 6 7 8 9 10 11
s a b a b \(\red{e}\) f a b a b _
next[0]=0
next[1]=0
next[2]=0
next[3]=1
next[4]=2
next[5]=0
next[6]=0
next[7]=1
next[8]=2
next[9]=3
next[10]=4

我们试着用动态规划的思想,已知 \(next_{1\sim10}\),求 \(next_{11}\)。

已知:\(s_{1\sim4}=s_{7\sim10}\)。

  • 如果 \(s_5=s_{11}=e\),那么 \(next_{11}=next_{10}+1\)。
  • 如果 \(s_{11}=a\),那么,取次长公共前后缀,就是 \(ab\),长度为 \(2\),就是 \(next[next[10]]\),刚好可以与 \(a\) 搭配,形成 \(aba\),则 \(next_{11}=next_{next_{10}}+1=3\)。
  • 如果 \(s_{11}=x\),什么都不是,那就只能是 \(0\)了。可以转化为不断地取次长公共前后缀,但是都不符合,最后变成 \(0\) 了而已。

综上,我们得出,只要 \(s_{i+1}\ne s_{r+1}\),就让 \(r\gets next_r\),前提是 \(r\ne0\)。

最后就让 \(next_{i+1}=next_{r}+1\) 就行咯。

代码:

inline void init() {
nex[0] = nex[1] = 0; // 初始化
int m = t.size();
for (int i = 1, j = 0; i < m; i++) { // 求 nex[i+1]
while (j && s[i + 1] != s[j + 1]) j = nex[j]; // 取次长
if (s[i + 1] == s[j + 1]) j++; // 为什么不统一+1?因为上面可能是因为j=0而终止的。
nex[i + 1] = j;
}
return;
}

例题

求出现位置与 \(next\) 数组。

代码:

// Problem: P3375 【模板】KMP
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3375
// Memory Limit: 512 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org) /*+ Nimbunny +*/ #include <bits/stdc++.h>
#define endl '\n'
#define pi pair<int, int>
// #define int long long
// #pragma GCC optimize(2) using namespace std;
const int INF = INT_MAX;
const int mod = 1e9 + 7;
const int N = 1e6 + 10;
string root, s;
int n, m, nex[N]; inline int read() {
int x;
cin >> x;
return x;
} inline void init() {
nex[0] = nex[1] = 0; // 初始化
for (int i = 1, j = 0; i < m; i++) { // 求 nex[i+1]
while (j && s[i + 1] != s[j + 1]) j = nex[j]; // 取次长
if (s[i + 1] == s[j + 1]) j++; // 为什么不统一+1?因为上面可能是因为j=0而终止的。
nex[i + 1] = j;
}
return;
} signed main() {
cin.tie(nullptr)->sync_with_stdio(false);
cin >> root >> s;
n = root.size(), m = s.size();
root = "#" + root, s = "#" + s;
init();
for (int l = 0, r = 0; l < n; l++) {
while (r && root[l + 1] != s[r + 1]) r = nex[r];
if (root[l + 1] == s[r + 1]) r++;
if (r == m)
cout << l + 1 - m + 1 << endl; // 因为l还没有+1,所以l要先+1
}
for (int i = 1; i <= m; i++) cout << nex[i] << " ";
return 0;
}

3.3 时间复杂度

鸽子咯咯咯子鸽。

[学习笔记] KMP算法——烤馍片(超详细)的更多相关文章

  1. 学习笔记-KMP算法

    按照学习计划和TimeMachine学长的推荐,学习了一下KMP算法. 昨晚晚自习下课前粗略的看了看,发现根本理解不了高端的next数组啊有木有,不过好在在今天系统的学习了之后感觉是有很大提升的了,起 ...

  2. [一本通学习笔记] KMP算法

    KMP算法 对于串s[1..n],我们定义fail[i]表示以串s[1..i]的最长公共真前后缀. 我们首先考虑对于模式串p,如何计算出它的fail数组.定义fail[0]=-1. 根据“真前后缀”的 ...

  3. 【学习笔记】:JavaScript基础知识超详细总结!

    目录 一.JavaScript的实现 二.JavaScript语言的特点 三.JS与HTML如何结合 四.JS中的数据类型 四.JS的原始数据类型 2.JS的引用数据类型 五.JS引用数据类型之函数 ...

  4. [ML学习笔记] XGBoost算法

    [ML学习笔记] XGBoost算法 回归树 决策树可用于分类和回归,分类的结果是离散值(类别),回归的结果是连续值(数值),但本质都是特征(feature)到结果/标签(label)之间的映射. 这 ...

  5. 【Redis】命令学习笔记——键(key)(20个超全字典版)

    安装完redis和redis-desktop-manager后,开始学习命令啦!本篇基于redis 4.0.11版本,从对键(key)开始挖坑! 准备工作,使用db1(默认db0,由于之前练习用db0 ...

  6. 学习笔记 - Manacher算法

    Manacher算法 - 学习笔记 是从最近Codeforces的一场比赛了解到这个算法的~ 非常新奇,毕竟是第一次听说 \(O(n)\) 的回文串算法 我在 vjudge 上开了一个[练习],有兴趣 ...

  7. 算法笔记--KMP算法 && EXKMP算法

    1.KMP算法 这个博客写的不错:http://www.cnblogs.com/SYCstudio/p/7194315.html 模板: next数组的求解,那个循环本质就是如果相同前后缀不能加上该位 ...

  8. 来去学习之---KMP算法--next计算过程

    一.概述 KMP算法是一种字符串匹配算法,比如现有字符串 T:ABCDABCDABCDCABCDABCDE, P:ABCDABCDE P字符串对应的next值:[0,0,0,0,1,2,3,4,0] ...

  9. 学习笔记——EM算法

    EM算法是一种迭代算法,用于含有隐变量(hidden variable)的概率模型参数的极大似然估计,或极大后验概率估计.EM算法的每次迭代由两步组成:E步,求期望(expectation):M步,求 ...

  10. 数据挖掘学习笔记--AdaBoost算法(一)

    声明: 这篇笔记是自己对AdaBoost原理的一些理解,如果有错,还望指正,俯谢- 背景: AdaBoost算法,这个算法思路简单,但是论文真是各种晦涩啊-,以下是自己看了A Short Introd ...

随机推荐

  1. 【数学建模经验分享】 | 2025美赛E题F奖论文分享 | 微分方程模型 | 种族竞争狩猎模型 | Lotka-Volterra 方程拓展

    0 前言 昨晚(准确说是今天)凌晨三点正在熬夜写计网作业突然收到队友发来消息说美赛成绩出来了,查了一下拿到F奖,感到非常欣喜.但欣喜之余其实也有一些对于没有拿到O奖的遗憾,毕竟在刚提交完论文的时候就感 ...

  2. 堆叠、MLAG、VPC、VSS 技术对比及架构建议

    堆叠.MLAG.VPC.VSS 技术对比及架构建议 1. 堆叠(Stacking) 技术实现: 多台物理设备通过专用堆叠线缆(如华为的Stack.华三IRF.思科StackWise)或普通光纤/以太网 ...

  3. WPF之X名称空间详解

    XAM简介: XAML是一种专门用于绘制UI的语言,借助它就可以把UI定义与运行逻辑分离开来.XAML使用标签来定义UI元素,每个标签对应.NET Framework类库中的一个控件类.通过设置标签的 ...

  4. 浅析Java8中default关键字

    摘要:介绍Java8新增关键字default,它用于在接口中标记方法为默认方法和编写实现逻辑,方便通过新增方法重构接口,而无需修改所有实现类,目的在于兼容接口已有实现类. 综述   default关键 ...

  5. Astah Community安装教程及使用说明(包括括菜单命令解释、操作向导说明、快捷命令说明)

    一.安装教程: 1.双击下载好的文件,允许安装(现在官网已经停刊免费的社区版了,如果想要下载社区版需要从其他地方寻找资源) 2.选择安装的语言,English,点击OK 3.next,接受协议,nex ...

  6. C++数据结构和算法代码模板总结——算法部分

    数据结构和算法学*了将*两周,及时总结和整理一下相关的知识点温故而知新.(一)C++双指针,有个经典的问题:荷兰国旗问题.[leetcode]75.颜色分类 public void sortColor ...

  7. wso2~自定义id_token

    https://medium.com/@vinula9/scope-allowlisting-whitelisting-in-wso2-api-manager-for-generating-acces ...

  8. Cocos3内置Effect(着色器)介绍

    创建材质后Effect有很多下拉选项,介绍一下: 在 Cocos Creator 中,材质(Material) 是用于定义物体表面渲染效果的资源,而 Effect(效果文件) 是材质的核心,它定义了如 ...

  9. CS与BS架构

    CS/BS C/S和B/S都是互联网中常见的网络结构模型. (1)什么是C/S模型 C/S模型指的是客户端/服务器模型,是一种计算机系统架构模式,其中系统功能被划分为客户端和服务器两个独立的部分,它们 ...

  10. CF958E1 题解

    Problem 原题链接 Meaning 在二维平面内,有位置不同且不存在三点共线的 \(R\) 个红点和 \(B\) 个黑点,判断是否能用一些互不相交的线段连接每一个点,使得每条线段的两端都分别是黑 ...