KMP算法

2023-05-16

 

一、何谓模式串匹配

模式串匹配,就是给定一个需要处理的文本串(理论上应该很长)和一个需要在文本串中搜索的模式串(理论上长度应该远小于文本串),查询在该文本串中,给出的模式串的出现有无、次数、位置等。

模式串匹配的意义在于,如果我是一个平台的管理员,我可以针对一篇文章或者一句话,搜索其中某个特定脏字或者不雅词汇的出现次数、位置——次数可以帮助我决定采取何种等级对于该用户的惩罚方式,而位置则可以帮助我给每一个脏词打上“*”的标记来自动屏蔽这些脏词。

二、浅析 KMPKMP 之思想

哦呵呵这个算法的名字比较诡异是因为有三位伟大的科学家共同设计完成……分别是 \mathcal{Knuth(D.E.Knuth) \& Morris(J.H.Morris)\& Pratt(V.R.Pratt)}Knuth(D.E.Knuth)&Morris(J.H.Morris)&Pratt(V.R.Pratt)

然而我并不知道他们是谁

首先要理解,朴素的单模式串匹配大概就是枚举每一个文本串元素,然后从这一位开始不断向后比较,每次比较失败之后都要从头开始重新比对,大概期望时间复杂度在 \Theta(n+m)Θ(n+m) 左右,对于一般的弱数据还是阔以跑的了滴。但是其实是可以被卡成 O(nm)O(nm) 的。 emmmmemmmm 并且还是比较容易卡的。

而 KMPKMP 的精髓在于,对于每次失配之后,我都不会从头重新开始枚举,而是根据我已经得知的数据,从某个特定的位置开始匹配;而对于模式串的每一位,都有唯一的“特定变化位置”,这个在失配之后的特定变化位置可以帮助我们利用已有的数据不用从头匹配,从而节约时间。

比如我们考虑一组样例:

模式串:abcab
文本串:abcacababcab

首先,前四位按位匹配成功,遇到第五位不同,而这时,我们选择将 abcababcab 向右移三位,或者可以直接理解为移动到模式串中与失配字符相同的那一位。可以简单地理解为,我们将两个已经遍历过的模式串字符重合,导致我们可以不用一位一位地移动,而是根据相同的字符来实现快速移动。

模式串:   abcab
文本串:abcacababcab

但有时不光只会有单个字符重复:

模式串:abcabc
文本串:abcabdababcabc

当我们发现在第六位失配时,我们可以将模式串的第一二位移动到第四五位,因为它们相同 qwqqwq .

模式串:   abcabc
文本串:abcabdababcabc

那么现在已经很明了了, KMPKMP 的重头戏就在于用失配数组来确定当某一位失配时,我们可以将前一位跳跃到之前匹配过的某一位。而此处有几个先决条件需要理解:

1、我们的失配数组应当建立在模式串意义下,而不是文本串意义下。因为显然模式串要更加灵活,在失配后换位时,更灵活简便地处理。

2、如何确定位置呢?

首先我们要明白,基于先决条件 11 而言,我们在预处理时应当考虑当模式串的第 ii 位失配时,应当跳转到哪里.因为在文本串中,之前匹配过的所有字符已经没有用了——都是匹配完成或者已经失配的,所以我们的 kmpkmp 数组(即是用于确定失配后变化位置的数组,下同)应当记录的是:

在模式串 str1str1 中,对于每一位 str1(i)str1(i) ,它的 kmpkmp 数组应当是记录一个位置 jj , j \leq iji 并且满足 str1(i)=str1(j)str1(i)=str1(j) 并且在 j!=1j!=1 时理应满足 str1(1)str1(1) 至 str1(j-1)str1(j1) 分别与 str(i-j+1)str(ij+1) ~ str1(i-1)str1(i1) 按位相等

上述即为移位法则

3、从前缀后缀来解释 KMPKMP :

首先解释前后缀(因为太简单就不解释了 qwqqwq ):

给定串:ABCABA
前缀:A,AB,ABC,ABCA,ABCAB,ABCABA
后缀:A,BA,ABA,CABA,BCABA,ABCABA

其实刚才的移位法则就是对于模式串的每个前缀而言,用 kmpkmp 数组记录到它为止的模式串前缀的真前缀和真后缀最大相同的位置(注意,这个地方没有写错,是真的有嵌套 qwqqwq )。然而这个地方我们要考虑“模式串前缀的前缀和后缀最大相同的位置”原因在于,我们需要用到 kmpkmp 数组换位时,当且仅当未完全匹配。所以我们的操作只是针对模式串的前缀 --− 毕竟是失配函数,失配之后只有可能是某个部分前缀需要“快速移动”。所以这就可以解释 KMPKMP 中前后缀应用的一个特点:

KMPKMP 中前后缀不包括模式串本身,即只考虑真前缀和真后缀,因为模式串本身需要整体考虑,当且仅当匹配完整个串之后;而匹配完整个串不就完成匹配了吗 qwqqwq

三、代码实现

1、 kmp[i]kmp[i] 用于记录当匹配到模式串的第 ii 位之后失配,该跳转到模式串的哪个位置,那么对于模式串的第一位和第二位而言,只能回跳到 11 ,因为是 KMPKMP 是要将真前缀跳跃到与它相同的真后缀上去(通常也可以反着理解),所以当 i=0i=0 或者 i=1i=1 时,相同的真前缀只会是 str1(0)str1(0) 这一个字符,所以 kmp[0]=kmp[1]=1kmp[0]=kmp[1]=1 。

2、对于如何和文本串比对,很简单:


 int j;
    j=0;//j可以看做表示当前已经匹配完的模式串的最后一位的位置 
    //如果楼上看不懂,你也可以理解为j表示模式串匹配到第几位了 
    for(int i=1;i<=la;i++)
       {
          while(j&&b[j+1]!=a[i])j=kmp[j];
          //如果失配 ,那么就不断向回跳,直到可以继续匹配 
          if (b[j+1]==a[i]) j++;
          //如果匹配成功,那么对应的模式串位置++ 
          if (j==lb) 
          {
          cout<<i-lb+1<<endl;
          j=kmp[j];
          //继续匹配 
          }
       }  

3、那么我们该如何处理 kmpkmp 数组呢?我们可以考虑用模式串自己匹配自己


 j=0;
    for (int i=2;i<=lb;i++)
       {     
       while(j&&b[i]!=b[j+1])
       //此处判断j是否为0的原因在于,如果回跳到第一个字符就不 用再回跳了
       j=kmp[j];    
        //通过自己匹配自己来得出每一个点的kmp值 
       if(b[j+1]==b[i])j++;    
       kmp[i]=j;
        //i+1失配后应该如何跳 
       }  

那么这个“自己匹配自己”该如何理解呢?我们可以这么想: 首先,在单次循环只有一个 ifif 来判断的原因在于每次至多向后多求一位的 nextnext ;

并且 jj 是拥有可继承性的,由于 jj 是用于比对前缀后缀的,那么对于一组前后缀而言,第 i-1i1 和第 j-1j1 位之前均相同或者有不同,决定着 ii 和 jj 匹配的结果是从 00 开始还是基于上一个 jj 继续 ++++

贴标程:


#include<iostream>
#include<cstring>
#define MAXN 1000010
using namespace std;
int kmp[MAXN];
int la,lb,j; 
char a[MAXN],b[MAXN];
int main()
{
    cin>>a+1;
    cin>>b+1;
    la=strlen(a+1);
    lb=strlen(b+1);
    for (int i=2;i<=lb;i++)
       {     
       while(j&&b[i]!=b[j+1])
        j=kmp[j];    
       if(b[j+1]==b[i])j++;    
        kmp[i]=j;
       }
    j=0;
    for(int i=1;i<=la;i++)
       {
          while(j>0&&b[j+1]!=a[i])
           j=kmp[j];
          if (b[j+1]==a[i]) 
           j++;
          if (j==lb) {cout<<i-lb+1<<endl;j=kmp[j];}
       }

    for (int i=1;i<=lb;i++)
    cout<<kmp[i]<<" ";
    return 0;
}  

 

那么时间复杂度为 \Theta(m+n)Θ(m+n) ,比朴素算法有了极大的优化。


Extra \ \ KnowledgeExtra  Knowledge 浅析复杂度证明

题外话:本来想扯摊还分析来着,但是 rqyrqy 说的好像比较直接易懂,于是在这里就引用了 rqyrqy 的话:

每次位置指针 i++i++ 时,失配指针 jj 至多增加一次,所以 jj 至多增加 lenlen 次,从而至多减少 lenlen 次,所以就是 \Theta(len_N + len_M) = \Theta(N + M)Θ(lenN+lenM)=Θ(N+M) 的

总之很迷就对了(逃


其实我们也可以发现, KMPKMP 算法之所以快,不仅仅由于它的失配处理方案,更重要的是利用前缀后缀的特性,从不会反反复复地找,我们可以看到代码里对于匹配只有一重循环,也就是说 KMPKMP 算法具有一种“最优历史处理”的性质,而这种性质也是基于 KMPKMP 的核心思想的。

 参考文章:

     https://pks-loving.blog.luogu.org/zi-fu-chuan-xue-xi-bi-ji-qian-xi-kmp-xuan-xue-di-dan-mu-shi-chuan-pi-post(本文主体)

     https://blog.csdn.net/gao506440410/article/details/81812163

 

 

 

 

 

 

转载于:https://www.cnblogs.com/SeanOcean/p/11229983.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

KMP算法 的相关文章

  • 串的模式识别和kmp算法

    串的简单模式匹配 与 KMP 获取next数组 参考书籍 王道 数据结构 代码验证软件 xff1a vs2019 include lt iostream gt typedef char SString 暴力比对 S abcabaaabaab
  • 数据结构串的基本操作及KMP算法

    将串的基本操作C语言实现 xff0c 实现KMP算法算出NEXT函数和NEXTVAL的值 SqString h的基本内容 span class hljs keyword typedef span span class hljs keywor
  • KMP算法

    一 何谓模式串匹配 模式串匹配 xff0c 就是给定一个需要处理的文本串 xff08 理论上应该很长 xff09 和一个需要在文本串中搜索的模式串 xff08 理论上长度应该远小于文本串 xff09 xff0c 查询在该文本串中 xff0c
  • ZJM 与纸条(KMP算法)

    问题描述 ZJM 的女朋友是一个书法家 xff0c 喜欢写一些好看的英文书法 有一天 ZJM 拿到了她写的纸条 xff0c 纸条上的字暗示了 ZJM 的女朋友 想给 ZJM 送生日礼物 ZJM 想知道自己收到的礼物是不是就是她送的 xff0
  • 【Week15作业 C】ZJM与纸条【KMP】

    题意 xff1a ZJM 的女朋友是一个书法家 xff0c 喜欢写一些好看的英文书法 有一天 ZJM 拿到了她写的纸条 xff0c 纸条上的字暗示了 ZJM 的女朋友 想给 ZJM 送生日礼物 ZJM 想知道自己收到的礼物是不是就是她送的
  • hdu 1358 Period KMP

    题目大意 xff1a 对于一个字符串 xff0c 找由循环字符串组成的位置 xff0c 并输出最多循环了几次 xff0c 比如两个样例 xff0c 第一个是 aaa xff0c 所以在第二个位置由子串a循环两次得到 xff0c 第三个位置由
  • 9.KMP算法

    KMP算法 1 KMP算法解决的问题 字符串str1和str2 xff0c str1是否包含str2 xff0c 如果包含返回str2在str1中开始的位置 xff0c 如果不包含返回 1 如果做到时间复杂度O N 完成 xff1f 测试用
  • Cyclic Nacklace 【HDU - 3746】【KMP补周期】

    KMP算法的讲解 自己的领悟可随时提问 题目链接 题意 有一个字符序列 现在问你 序列后面最少补充几个元素使其恰能成为几个重复循环的序列 题目还是很良心的 让我们求字符串后面放几个字符可以使其变成周期字符串 所以还是可以想到用KMP的nex
  • KMP算法详解

    一 什么是KMP算法 KMP主要应用在字符串匹配上 KMP的主要思想是当出现字符串不匹配时 通过已知一部分之前已经匹配的内容 避免从头再去做匹配 所以KMP算法的重点就是如何记录已经匹配的信息 也就是next 数组的实现 二 什么是next
  • KMP算法原理详解_论文解读版

    1 KMP算法 KMP算法是一种保证线性时间的字符串查找算法 由Knuth Morris和Pratt三位大神发明 而算法取自这三人名字的首字母 因而得名KMP算法 那发明这样的字符串查找算法又有什么用 在当时计算机本身非常昂贵 计算资源更是
  • Simpsons’ Hidden Talents【KMP模板题】

    Homer Marge I just figured out a way to discover some of the talents we weren t aware we had Marge Yeah what is it Homer
  • KMP算法之基础思想篇

    KMP算法是快速求字符串P 是不是字符串S的子串的一个算法 具体案例呢 可以看力扣的28题 实现 strStr 题意也很简单 就是找出P在S中出现的第一个位置 实际上就是找子串 这种最简单的方法就是暴力 直接两层for循环 O n m 的复
  • 使用共享 MVI 架构实现高效的 Kotlin Multiplatform Mobile (KMM) 开发

    使用共享 MVI 架构实现高效的 Kotlin Multiplatform Mobile KMM 开发 文章中探讨了 Google 提供的应用架构指南在多平台上的实现 通过共享视图模型 View Models 和共享 UI 状态 UI St
  • 求字符串可匹配的最大长度

    如 text abcdlijkfgd query abcdefg 最大匹配为 abcd 为4 编写一个函数 求字符串可匹配的最大长度 如果是完全匹配 则用很多种方法 如BF KMP sunday等字符串匹配算法 KMP是比较常见的 其思想也
  • 数据结构中Java实现KMP与BF算法对比

    public class KMPANDBF public int indexBfCount SeqString s SeqString t int begin int slen tlen i begin j 0 int count 0 sl
  • leetcode 028.实现strStr(),即查找重复字符串(KMP算法)

    前言 本题是经典的字符串单模匹配的模型 因此可以使用字符串匹配算法解决 常见的字符串匹配算法包括暴力匹配 Knuth Morris Pratt 算法 Boyer Moore 算法 Sunday 算法等 本文 前言 本题是经典的字符串单模匹配
  • BF,KMP,BM三种字符串匹配算法性能比较

    三种最基本的字符串匹配算法是BF KMP以及BM BF算法是最简单直接的匹配算法 就是逐个比较 一旦匹配不上 就往后移动一位 继续比较 所以比较次数很都 关于KMP和BM的详细介绍可以参考下面的两个link 是讲得比较好的 KMP http
  • 【扩展KMP】POJ_3450| HDU_2328 Corporate Identity

    原题直通车 POJ 3450 Corporate Identity HDU 2328 Corporate Identity 题意概述 找出N个串中最长公共子串 分析 一 可以直接枚举其中一个串的所有字串 跟所有串进行匹配找到结果 二 用其中
  • Count the string【KMP】

    It is well known that AekdyCoin is good at string problems as well as number theory problems When given a string s we ca
  • SDUTOJ KMP简单应用 【KMP】

    KMP简单应用 Time Limit 1000MS Memory limit 65536K 题目描述 给定两个字符串string1和string2 判断string2是否为string1的子串 输入 输入包含多组数据 每组测试数据包含两行

随机推荐

  • C++标准 — C++17特性 — 文件系统 — path 路径处理

    C 43 43 标准 C 43 43 17特性 文件系统 path 路径处理 一 分解二 查询三 拼接1 append 61 2 concat 43 61 四 修改五 遍历六 比较 类型 path 的对象表示文件系统上的路径 只有路径的语法
  • python编程语言创始人-Python编程语言的实现内幕的相关介绍

    以下的文章是对Python编程语言的 34 实现内幕 做一介绍 xff0c 大家很熟悉的有可能是Python的发展历史 xff0c 也有可能是Python编程语言的实际应用中具有强大的功能 xff0c 下面是文章的主要描述 xff0c 希望
  • python怎么安装包-如何给自己的Python项目制作安装包

    本教程将指导您如何打包一个简单的Python项目 它将向您展示如何添加必要的文件和结构来创建包 xff0c 如何构建包以及如何将其上载到Python包索引 A simple project 本教程使用名为example pkg的简单项目 如
  • python app教程-Python zipapp打包教程(超级详细)

    经过复杂的开发 调试之后 xff0c 终于得到一个 Python 程序 xff0c 这个程序或许精巧 xff0c 或许有些古拙 xff0c 但它是我们心血的结晶 xff0c 我们当然希望将这个程序发布出来 Python 提供了一个 zipa
  • python 文字语音朗读-怎么用 Python 来朗读网页 ?

    是不是有的时候懒得自己看新闻 xff1f 那么不妨试试用 Python 来朗读给你听吧 网页转换成语音 xff0c 步骤无外乎 xff1a 网页正文识别 xff0c 获取到正文的文本内容 xff1b 文本转语音 xff0c 通过接口将文本转
  • python 填充折线图下部区域

    整理一下 xff0c 运行图 xff1a 全部代码为 xff1a from pylab span class token function import span matplotlib rcParams span class token p
  • python语音在线编辑-Python:语音处理,实现在线朗读RFC文档或本地文本文件

    本文主要讲解如何使用python来实现将文本转为语音 xff0c 以一个小例子为例 xff0c 写了一下用pyTTS来朗读本地方件或在线朗读RFC文档 xff0c 当然也可以修改一下 xff0c 做成在线朗读新闻之类的 xff0c 另本来想
  • python画函数图像-python实现画出e指数函数的图像

    这里用Python逼近函数y 61 exp x 同样使用泰勒函数去逼近 exp x 61 1 43 x 43 x 2 2 43 43 x n n 43 usr bin python coding utf 8 import numpy as
  • python使用方法视频-使用Python进行视频处理

    Imageio逐帧视频处理 安装 conda install imageio 将视频转换成图片 import imageio timeF 61 10 reader 61 imageio get reader 39 imageio cocka
  • python认证证书有哪些-python考试认证

    广告关闭 腾讯云双11爆品提前享 xff0c 精选热门产品助力上云 xff0c 云服务器首年88元起 xff0c 买的越多返的越多 xff0c 最高满返5000元 xff01 除了之前热议的加入高考和中小学教育之外 xff0c 现在连普通大
  • python代码写完怎么运行-Python 项目代码写完了,然后怎么打包和发布?

    你把你的代码写完了 xff0c 是不是要给别人使用下 xff0c 怎么打包你的项目代码呢 喂 xff0c 开源么 接下来小帅b就跟你说说 xff0c 如何打包你的代码 就拿我们上次演示的 用 Python 开发一个 个人计划 todolis
  • Zoom to Learn, Learn to Zoom

    Abstract 本文表明 xff0c 将机器学习应用于数字变焦时 xff0c 对真实 原始的传感器数据进行操作是有益的 现有的基于学习的超分辨率方法不使用真实的传感器数据 xff0c 而是对经过处理的RGB图像进行操作 我们表明 xff0
  • 6个非常实用的 Python 代码块,适合收藏~

    大家好 xff0c 今天分享几个平时我会用到的 Python 代码块 xff0c 每个都小而精 xff0c 喜欢记得关注 点赞 收藏 1 xff0c 批量修改文件名 日常工作中 xff0c 可能会有这样的需求 xff1a 把一个文件夹下所有
  • 1.音视频播放原理介绍

    音视频技术主要包含以下几种 xff1a 封装技术 xff0c 视频压缩技术 xff0c 音频压缩技术 xff0c 流媒体协议技术以及防盗链技术 接下来的几篇文章将对这几种技术做深入的研究和实践 下面简单说明一下视频播放的原理 xff08 以
  • 码云仓库建库

    方法一 xff1a 先将在码云上新建的仓库clone到本地 xff0c 修改后再push到码云仓库 git clone https gitee com 用户个性地址 工程名字 git 将远程仓库克隆到本地 在克隆过程中 xff0c 如果仓库
  • Python 画多图 统计直方图

    画直方图的命令是这个 xff1a 把里面的内容改了就可以 ec参数调整的是edgecolor xff0c 即框线颜色 matplotlib pyplot hist span class token punctuation span x sp
  • 输入2个整数,求最大公约数和最小公倍数

    输入2个整数 xff0c 求最大公约数和最小公倍数 关于最大公约数的算法 xff0c 古希腊数学家欧几里得已经在2200年前给出我们算法公式 xff0c 我们直接拿来用就可以了 欧几里得算法也被称为辗转相除法 xff0c 用来求最大公约数
  • WSL2 安装 图形系统 及遇到的坑

    wsl本身不带有图形界面 xff0c 需要自己安装 安装流程如下 xff1a 一 windows环境安装VcXsrv 默认安装即可 二 Ubuntu环境安装 xfce4 sudo apt get install xfce4 三 Ubuntu
  • 【美团】项目学习1:登录逻辑实现

    rest framework 和app应用 INSTALLED APPS span class token operator span span class token punctuation span span class token s
  • KMP算法

    一 何谓模式串匹配 模式串匹配 xff0c 就是给定一个需要处理的文本串 xff08 理论上应该很长 xff09 和一个需要在文本串中搜索的模式串 xff08 理论上长度应该远小于文本串 xff09 xff0c 查询在该文本串中 xff0c