重学 KMP 小记

news/2025/1/16 0:03:11/文章来源:https://www.cnblogs.com/holmes-wang/p/18341183

重学 KMP 小记

前言

KMP 这个东西赛时用到的几率很小(虽然圣人说概率不小、也不是很大),但是如果一旦考字符串类的题又极可能考匹配问题。当时掌握得也是一知半解,所以现在来重学来了。

情境引入

现实中我们会遇到类似的问题:

给你一篇报道,让你找一找这篇报道中有没有出现某个人的名字。

形式化地,可以说:

给你文本串 \(S\),和模式串 \(T\),判断 \(T\) 是否为 \(S\) 的子串。

这个问题我们暴力地想,可以用两个指针 \(i\)\(j\) 分别表明现在匹配到 \(S\)\(T\) 的哪个位置了(\(0\le i< len_S\)\(0\leq j<len_T\))。如果 \(S_i\neq T_j\),则 \(i\leftarrow i-j+1\)\(j\leftarrow 0\)。相当于是推翻重来。

有没有优美一点的算法呢?答案是有的,就是我们的主角——KMP。

算法概要

我们在暴力的时候,如果一旦失配,模式串的指针 \(j\) 就又从头开始,这显然是非常浪费的。所以我们如果想降低时间复杂度,就要从这里入手。

首先我们定义一个数组 \(next_i\),其满足:\(S_{[0,next_i-1]}=S_{[i-next_i,i]}\)\(S_{[l,r]}\) 表示 \(S_l,S_{l+1},\dots,S_{r}\) 组成的子串。当然这个 \(next_i\) 有很多种情况,我们储存的是子串最长的情况。说白了这两部分子串就是 \(S_{[0,i]}\) 的最长公共前后缀。

特别地,\(next_0=-1\)

接下来就可以引入 KMP 了,算法流程如下:

  1. 如果 \(S_i\)\(T_{j+1}\) 匹配成功,即相同,就 \(i\leftarrow i+1\)\(j\leftarrow j+1\),继续匹配。
  2. 如果失配,则令 \(i\) 不动,\(j\leftarrow next_j\)。这意味着 \(S\) 不变,将整个 \(T\) 向右移动了 \(j-next_j\) 位。这个值肯定是大于等于 \(1\) 的。

这样就没了。

现在来分析一下这个 KMP 是怎么减少浪费的。

\(T\) 匹配到 \(j\) 位时,说明前面都是和 \(S\) 相同的。如果此时失配了,暴力的思想是相当于直接把 \(T\) 向右平移一位,然后重新比较。这样显然没有前途。KMP 是怎么做的呢?KMP 的思想是:“既然我这个 \(T_{[0,j]}\) 里可能有公共前后缀,如果有的话,为什么我不直接把 \(T\) 向右平移至这个最长公共前后缀相同的部分呢?”。

画个草图理解一下:

图中蓝色的部分都相同。

如何预处理 \(next_i\) 数组

考虑递推。现假设 \(next_{[0,i-1]}\) 的元素都已求出。

算法过程就很简单了:

  1. 如果 str[i]==str[next[i-1]+1],则 next[i]=next[i-1]+1
  2. 否则判断 str[i]str[next[next[i-1]]+1] 是否相等。
  3. 再否则,判断 str[i]str[next[next[next[i-1]]]+1] 是否相等。
  4. 回环往复,直至相等或 \(next\) 的值为 \(0\) 为止。
void getnxt()
{int j=0;for(int i=2;i<=m;i++){while(j&&b[j+1]!=b[i])j=nxt[j];if(b[j+1]==b[i])j++;nxt[i]=j;}
}

例题展现

P3375 【模板】KMP

#include<bits/stdc++.h>
using namespace std;#define int long longconst int MAXN=1e6+5;int n,m;
char a[MAXN];
char b[MAXN];
int nxt[MAXN];void getnxt()
{int j=0;for(int i=2;i<=m;i++){while(j&&b[j+1]!=b[i])j=nxt[j];if(b[j+1]==b[i])j++;nxt[i]=j;}
}void kmp()
{for(int i=1,j=0;i<=n;i++){while(j&&a[i]!=b[j+1])j=nxt[j];if(b[j+1]==a[i])j++;if(j==m){printf("%lld\n",i-m+1);j=nxt[j];}}
}signed main()
{scanf("%s%s",a+1,b+1);n=strlen(a+1),m=strlen(b+1);getnxt();kmp();for(int i=1;i<=m;i++)printf("%lld ",nxt[i]);return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/777370.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Manhattan Triangle

纪念一下代码打得太慢了导致比赛结束3分钟才做出来的E题 我的做法: 考虑确定枚举三角形的一个点。最开始尝试枚举\(x\)最大的点,但是后面发现不太好讨论,于是尝试枚举\(x\)在中间的点,此时发现由于曼哈顿是三角形不可能是钝角三角形,剩下两个点要么同时在中间点的上方,要…

JAVA游戏源码:天天酷跑

学习java朋友们,福利来了,今天小编给大家带来了一款天天酷跑源码。注意:此源码仅供学习使用!! 源码搭建和讲解 启动main入口://************************************************************************ // ************完整源码移步: gitee典康姆/hadluo/java_game01…

从零开始的JAVAday29~day35

后续语法if()语法 若满足()中的语法,则执行后面的语句。循环for(a;b;c)和while(c)语法 for(a;c;b)语法意思为在循环前进行a语句每次循环结束后进行b语法,若满足c语句则再次循环。whlie(c)循环若满足c条件则循环。

Unittest框架的介绍及使用

介绍 基本概念 ​ unittest是Python自带的一个单元测试框架, 它可以做单元测试, 也能用于编写和运行重复的测试工作。它给自动化测试用例开发和执行提供了丰富的断言方法, 判断测试用例是否通过, 并最终生成测试结果. 四大组件test case:就是我们的测试用例,unittest中提供了…

Docker常用容器安装

docker安装与卸载以及使用docker安装常用容器Docker安装 安装docker(centos) docker引擎安装官网地址:Install Docker Engine on CentOS | Docker Documentation 手动安装 # 卸载旧版本 sudo yum remove docker \ docker-client \ docker-client-latest \ docker-common \ doc…

探究spring中如何如何从ioc中拿到对象,ioc中都存了什么

引言ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");Monster monster01 = (Monster)ioc.getBean("monster01");这是我们接触spring时接触的第一行代码,我们只是习惯性的通过ioc.getBean获取我们需要的对象,但是你有没有想过…

服务器性能分析软件「 WGCLOUD 」完整功能介绍

WGCLOUD是一款国产开源的服务器状态性能监测软件,免费高效,轻量实用,部署简单,上手操作容易,颜值在线,更好的是它具有极低的资源占用WGCLOUD官网下载地址:www.wgstart.com WGCLOUD可以支持哪些操作平台,如下: Linux:Debian,RedHat,CentOS,Ubuntu,Fedora,SUSE,麒…

Day13

逻辑运算符: &&都真为真 ||一真为真 !真为假假为真 短路运算:若两个运算中前一个为假,则不会执行后一个运算 二进制运算 A&B 相同为本,不同为0 A/B 相同为本,不同为1 A^B 相同为0 不同为1 ~B取反 位运算效率最高 << 左移*2 >>右…

Linux 防火墙系统

iptables 和 nftables iptables 是 Linux 中最常用的防火墙工具,它通过 Linux 内核中的 netfilter 模块提供的 Hook 来管理网络数据包的处理和转发。 nftables 是 iptables 的代替品,在 Debian 10、Ubuntu 22、CentOS 8 中已经由 iptables 切换到了 nftables。 iptables 的操…

[计算机网络]HTTPS

HTTP 与 HTTPS 有哪些区别? HTTP 是超文本传输协议,信息是明文传输,存在安全风险的问题。HTTPS 则解决 HTTP 不安全的缺陷,在 TCP 和 HTTP 网络层之间加入了 SSL/TLS 安全协议,使得报文能够加密传输。 HTTP 连接建立相对简单, TCP 三次握手之后便可进行 HTTP 的报文传输。…

腾讯云资源分页获取不全问题

如下,之前把offset值当成页码来使用了。偏移量实际是从第几条开始。 第一页的是从0*页码开始,偏移0*页码数 第二页是从1*页码开始,偏移1*页码数 依此类推。所以偏移量这里,当时只是填写页码,是不对的params = { "ZoneId": ZoneId, "Offset": cu…