首页 > 其他分享 >KMP 详解

KMP 详解

时间:2023-01-19 21:34:09浏览次数:52  
标签:匹配 ll 模式 详解 KMP 字符串 define

简介

KMP 这个名字的由来:它是三个人:D.E · Knuth 、J.H · Morris 和 V.R · Pratt 同时发现的。

KMP 是一种字符串单模匹配算法,复杂度 \(\operatorname{O(n+k)}\) 。
其中, \(n\) 是主串的长度, \(k\) 是模式串的长度。由于至少要遍历主串和模式串的每个字符,此类算法的最佳复杂度是 \(\operatorname{O(n+k)}\) ,KMP 差不多达到了此类算法能达到的最佳时间复杂度。

实现

模板题(AcWing.831

题目描述

给定一个字符串 \(s\),以及一个模式串 \(p\),所有字符串中只包含大小写英文字母以及阿拉伯数字。求出模式串 \(p\) 在字符串 \(s\) 中所有出现的位置的起始下标。

  • 模式串 \(p\) 在字符串 \(s\) 中多次作为子串出现。

样例

in:

3
aba
5
ababa

out:

0 2

KMP 实现

思路

首先想想暴力。
用 \(flag\) 表示是否匹配。先设 \(flag=1\) , 再用 \(i\) 枚举起始位置, \(j\) 枚举模式串的每个字符下标,如果有不匹配的,则 \(flag=0\) 。

for(ll i=1;i<=n;i++)
{
    bool flag=1;
    for(ll j=1;j<=m;j++)
        if(s[i]!=p[j]){flag=0;break;}
}

接下来要把它优化。

暴力匹配算法在每个字符失配后都回溯到了 \(i\) 的位置,显然这会浪费很多时间。
如果我们找到模式串 \(p\) 中两个相同的字串,每次匹配就可以从前一个子串
这样一来就可以做到只回溯 \(j\) 。(这里读者自己画图理解,这里很关键)

下图演绎了 KMP 如何匹配主串和模式串(原创的图)。

image

动态演示:

注:如果用 #include <bits/stdc++.h> ,那就不能用 next 做数组名,我在这里用的是 nxt

匹配代码

for(ll i=1,j=0;i<=m;i++)
{
    while(j&&s[i]!=p[j+1]) j=nxt[j];
    if(s[i]==p[j+1]) j++;
    if(j==n){write(i-n),putchar(' '),j=nxt[j];}
}

那怎么知道 \(j\) 回溯到哪去呢?
可以预处理出一个 \(next\) 数组,用来维护每个 \(j\) 回溯的位置。

下图演绎了如何预处理出 \(next\) 数组。

预处理代码

inline void init()
{
    for(ll i=2,j=0;i<=n;i++)
    {
        while(j&&p[i]!=p[j+1]) j=nxt[j];
        if(p[i]==p[j+1]) j++;
        nxt[i]=j;
    }
}

代码

#include <bits/stdc++.h>
#define ll long long
#define rll register ll
#define cll const ll
#define N 1000005
using namespace std;
inline ll read()
{
    rll x=0;bool f=1;static char c=getchar();
    while(!isdigit(c)){if(c=='-') f=0;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return (f==1)?x:-x;
}
inline void write(ll x)
{
    if(x<0) putchar('-'),x=-x;
    if(x>9) write(x/10);
    putchar(x%10+48);
}//卡常
ll n=read(),m,nxt[N];
char p[N],s[N];
inline void init()
{
    for(rll i=2,j=0;i<=n;i++)
    {
        while(j&&p[i]!=p[j+1]) j=nxt[j];
        if(p[i]==p[j+1]) j++;
        nxt[i]=j;
    }
}//初始化next数组
int main()
{
    scanf("%s",p+1);m=read();scanf("%s",s+1);
    init();
    for(rll i=1,j=0;i<=m;i++)
    {
        while(j&&s[i]!=p[j+1]) j=nxt[j];
        if(s[i]==p[j+1]) j++;
        if(j==n){write(i-n),putchar(' '),j=nxt[j];}
    }
    return 0;
}

标签:匹配,ll,模式,详解,KMP,字符串,define
From: https://www.cnblogs.com/wangxuzhou-blog/p/17062183.html

相关文章

  • Target EDI 对接详解 – ECGrid AS2 连接
    Target塔吉特是美国仅次于Walmart沃尔玛的第二大巨型折扣零售百货集团。Target在2020财年实现零售收入同比增长19.8%,赶超了CVS和Tesco,并在2020财年的销售额增长超过......
  • KMP算法详解(逻辑分析&数学证明&代码实现)
    前言KMP算法是Knuth、Morris、Pratt三人在BF算法的基础上同时提出的模式匹配的高效算法。本文以字符串匹配问题为例,以通俗易懂的语言对KMP算法进行逻辑分析、数学证明和代码......
  • 单调队列详解
    简介单调栈和单调队列都是思维难度比较大的数据结构,但只要想明白了就会觉得很简单。要理解单调队列,首先得明白“单调”是指它存储的内容单调,而不是指它简单。实现模板......
  • DBNet源码详解
    参考项目:https://github.com/WenmuZhou/DBNet.pytorch标签制作制作thresholdmap标签make_border_map.py程序入口if__name__=='__main__'if__name__=='__main......
  • 【并发编程】ThreadLocal详解
    文章目录​​1.ThreadLocal简介​​​​2.ThreadLocal的简单使用​​​​3.ThreadLocal的实现原理​​​​4.ThreadLocal不支持继承性​​​​5.InheritableThreadLocal支持......
  • OpenCV Mat类详解
    1.Mat类常用成员函数和成员变量        由于Mat类使用的非常广泛,使用的形式也非常之多,这里只对较为常用的成员函数和成员变量做出了整理;1.1构造函数(1)默认构......
  • 分块入门详解(比较全面)
    最近写了几个分块,顺便做一下笔记。什么是分块分块是一种数据结构。。有许多数据结构都是\(\mathrm{log}\)数据结构,比如线段树,树状数组等等。他们复杂度优秀,但是都是树......
  • pikachu-CSRF跨站请伪造通关详解
    pikachu-CSRF跨站请伪造通关详解一、getburp抓包修改信息页面,可以看到请求的url修改的数据都在url中,我们可以尝试在url中修改,之后再去访问url此时的数据内容:将url中......
  • 初探attention—attention原理和代码详解
    attention在正式开始探索attention之前,首先了解一下seq2seq。循环神经网络只能将一个序列信号转换为定长输出,但Seq2Seq可以实现一个序列信号转化成一个不定长的序列输出,因......
  • 单调栈详解
    简介单调栈和单调队列都是思维难度比较大的数据结构,但是比较幸运,这些数据结构能出的题不多,只有几个板子。要理解单调栈,首先得明白“单调”是只它存储的内容单调,不是说它......