[笔记](更新中)KMP算法 - 下(例题)

news/2025/1/16 2:37:53/文章来源:https://www.cnblogs.com/Sinktank/p/18339316

CF1200E Compress Words ~ 洛谷

给定\(n\)个字符串,请按下面的规则,从左往右依次合并\(n\)个字符串,成为\(1\)个字符串:

  • \(A,B\)合并,就是找到最大的\(i\),使得\(A\)的长为\(i\)的后缀和\(B\)的长为\(i\)的前缀相等,删除\(A\)的这个后缀,并将\(B\)连接到它的后面。

注意每次应该将第\(i\)个字符串与\(1\sim (i-1)\)合并后的结果进行新的一轮合并,而非输入字符串之间合并。

\(n\le 10^5\),字符串总长\(\le 10^6\)

我们考虑\(A,B\)合并,其实就是找到\(A\)的后缀 和 \(B\)的前缀的最长共同部分,然后删掉其中一个,再把\(A,B\)连接起来。比如ABCDCDCDCDEF的共同部分就是那个CDCD

怎么找这个共同部分呢?

我们设T=B+'~'+A,那么\(T\)的最长公共前后缀就是我们要求的最长公共部分,而这个最长公共前后缀可以直接套用KMP的\(nxt\)数组。

为什么要隔一个~呢?因为不隔的话,我们找的公共部分会重叠,导致错误。

还需要注意的是,如果每次都把当前的答案\(S\)和新字符串\(A\)连接在一起,时间复杂度就是\(O(n^2)\)。而我们知道公共部分的长度就是\(\min(\)S.size()\(,\)A.size()\()\),所以应该先截取一下,让T.size()\(<2*\)A.size()。这样时间复杂度就是\(O(n)\)了。

点击查看代码
#include<bits/stdc++.h>
#define N 1000010
using namespace std;
int t,n,m,nxt[N];
string s,a;
int main(){cin>>t;while(t--){cin>>a;n=s.size(),m=a.size();//为了拼接更方便,下标从0开始int minlen=min(n,m);string ts=a.substr(0,minlen)+'~'+s.substr(n-minlen,minlen);int tn=ts.size();for(int i=1,j=0;i<tn;i++){while(ts[i]!=ts[j]&&j) j=nxt[j-1];nxt[i]=(ts[i]==ts[j]?++j:0);//别忘了置0}//nxt[tn-1]就是重合部分的长度s+=a.substr(nxt[tn-1],m);//小技巧,超出边界自动调整}cout<<s<<"\n";return 0;
}

其中代码第\(18\)行用到一个小技巧,string.substr(pos,size),意为从\(pos\)开始截取\(size\)个字符,如果\(pos+size-1\)超过右边界,会自动调整到右边界,不要误以为代码中的\(m\)就是右边界哦。
还有,参数\(size\)是允许超界的,但如果参数\(pos>\)string.size()会RE!

另外,这道题也可以用字符串哈希做,不过KMP比哈希快一倍(可能是因为字符串哈希要双哈希,所以有\(2\)倍常数)。

P4824 [USACO15FEB] Censoring S

给定字符串\(A,B\),请重复下面的操作,直到\(A\)中不存在\(B\)

  • 删除\(A\)中最靠前的\(B\)

请输出最后的\(A\)

(注意删除\(B\)之后,两端的字符串可能拼接成一个新的\(B\)

\(1\le |B|\le |A|\le 10^6\)

我们可以想到,建立一个栈,依次加入\(i\)指针。如果中途凑成一个\(B\)则把这些元素删掉。而判断凑成\(B\)就可以用KMP。

在KMP的过程中进行比对,如果凑出一个\(B\),就把匹配的下标全部出栈,所谓删掉,其实就是把\(j\)指针回溯成之前的状态,即变成出栈后栈顶\(i\)指针所对应的\(j\)。所以我们用\(t[i]\)来表示\(i\)指针所对应的\(j\)指针,出栈后\(j\)设为\(t[st[top]]\)即可。

点击查看代码
#include<bits/stdc++.h>
#define N 1000010
using namespace std;
string a,b;
int n,m,nxt[N],t[N],st[N],top;
int main(){cin>>a>>b;n=a.size(),m=b.size();for(int i=1,j=0;i<m;i++){while(b[i]!=b[j]&&j) j=nxt[j-1];nxt[i]=(b[i]==b[j]?++j:0);}for(int i=0,j=0;i<n;i++){while(a[i]!=b[j]&&j) j=nxt[j-1];t[i]=j,st[++top]=i;if(a[i]==b[j]){if(j==m-1) top-=m,j=t[st[top]];j++;}}for(int i=1;i<=top;i++) cout<<a[st[i]];return 0;
}

P3435 [POI2006] OKR-Periods of Words

给定字符串\(S\),请求出\(S\)每个前缀的答案之和。

定义字符串\(T\)的答案为:

  • 如果存在字符串\(P\)\(T\)真前缀,且\(T\)\(P+P\)前缀,则答案就是\(P\)可能的最长长度。
  • 如果不存在字符串\(P\),则答案为\(0\)

题面看上去有点绕,但其实找一找规律:

如上图,红色部分是整个字符串的答案。

可以发现黑色部分其实就是这个字符串的“最短非空公共前后缀”。

所以先跑一遍KMP的前半部分,把\(nxt\)数组求出来。

然后遍历每个前缀,不断沿着\(nxt\)数组往前跳,直到找到该前缀的“最短非空公共前后缀”长度,记为\(j\)

对于长度为\(i\)的前缀,如果\(j=0\),则答案不累加;否则答案累加\(i-j\)即可。

但是这样做会超时,不妨利用记忆化的思想,\(j\)每跳一下,就把\(nxt[i]\)更新为最新的\(j\),这样其他\(j\)再跳到\(i\)这个位置就能直接得到答案了。

点击查看代码
#include<bits/stdc++.h>
#define N 1000010
using namespace std;
int n,nxt[N];
long long ans;
string s;
int main(){cin>>n>>s;s=' '+s;//下标从1开始for(int i=2,j=1;i<=n;i++){while(s[i]!=s[j]&&j>1) j=nxt[j-1]+1;if(s[i]==s[j]) nxt[i]=j++;}for(int i=1;i<=n;i++){int j=nxt[i];while(nxt[j]) j=nxt[i]=nxt[j];if(j) ans+=i-j;}cout<<ans<<"\n";return 0;
}

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

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

相关文章

5个好看的vue3后台管理模板

- 1.SoybeanAdmin https://soybeanjs.cn 简介 SoybeanAdmin 是一个清新优雅、高颜值且功能强大的后台管理模板,基于最新的前端技术栈,包括 Vue3, Vite5, TypeScript, Pinia 和 UnoCSS。采用 Vue3, Vite5, TypeScript, Pinia 和 UnoCSS 等最新流行的技术栈。 采用 pnpm monor…

(全)Python 的虚拟环境构建和jupyter notebook 中虚拟环境切换

1、 在开始菜单打开Anaconda Prompt(anaconda3) 2、 构建虚拟环境语法:conda create --name 虚拟环境名字 python=版本号 conda create --name py38_env02 python=3.83、激活虚拟环境 语法: conda activate 虚拟环境名称 4、环境变量的配置 “我的电脑”→右键→ 属性 …

2. vite 构建项目

目录vite 构建项目1 安装node.js2 创建项目 vite 构建项目 1 安装node.js 下载链接 https://nodejs.p2hp.com/ 版本需大于 18.0 查看版本 node -v 添加环境变量npm(包管理工具, 类似于python的pip) npm -g (查看包安装位置) npm install (安装在项目的node_model下) npm inst…

[vue3] patchFlags与位运算

Vue 3在编译template过程中,会通过patchFlags优化虚拟DOM更新,提升性能。这些标志通过位运算进行操作,包括动态文本、类、样式、属性、静态提升等。patchFlags的使用极大地提高了diff算法的效率。Vue3在编译template的过程中会分析模板中的动态部分和静态部分,并标记相应的…

进度报告10

(1)1.代码块2.继承:用extends关键字,不支持多继承

2024-08-03:用go语言,给定一个从 0 开始的字符串数组 `words`, 我们定义一个名为 `isPrefixAndSuffix` 的布尔函数,该函数接受两个字符串参数 `str1` 和

2024-08-03:用go语言,给定一个从 0 开始的字符串数组 words, 我们定义一个名为 isPrefixAndSuffix 的布尔函数,该函数接受两个字符串参数 str1 和 str2。 当 str1 同时是 str2 的前缀和后缀时,函数返回 true;否则返回 false。 例如,isPrefixAndSuffix("aba", …

AGC064B 题解

设红色的点值为 0,蓝色为 1。 注意到,如果有一条边的颜色和两端点同色,一定可以选。例子:选择和两端点同色的边。 又发现,如果存在一个 \(sz>1\) 的合法连通块,无论和其他点怎么连,原来的这个连通块内的点一定合法。 有注意到形如 \(0\xleftrightarrow 10,1\xleftrig…

Java源码:泡泡堂

学习java朋友们,福利来了,今天小编给大家带来了一款泡泡堂源码。此源码仅供学习使用!! 源码搭建和讲解 代码简介包名称类描述com.whh.frame MyJFrame.java 显示窗体、绑定监听、启动线程com.whh.frame MyJPanel.java 不断读取人物信息com.whh.main GameStart.java 游戏入口…

24暑假集训day5上午

图论 差分约束 有 \(𝑛\) 个整数变量 \(𝑥_1∼𝑥_𝑛\)。 给定一些形如 \(𝑥_𝑖+𝑐≥𝑥_𝑗\) 的限制。问有没有可行解,如有输出方案。 例如 \(𝑥_1−1≥𝑥_2,𝑥_2≥𝑥_3,𝑥_3≥𝑥_1\) 就无解。 在单源最短路问题中,如果存在一条 \(𝑖→𝑗\)…

24暑假集训day4上午下午

基础图论 图的存储方式 无向边可以拆成两条有向边 1. 邻接矩阵 邻接矩阵:若 \(𝑖→𝑗\) 存在有向边,则令矩阵 \(𝐴[𝑖][𝑗]=1\)。 遍历一个点的所有出边是 \(𝑂(𝑛)\) 的。 空间复杂度 \(𝑂(𝑛^2 )\)。 总结:复杂度太高,尽量不使用 bool hasEdge[MAXN][M…

微客在线客服系统-连接一切客户

嘿,朋友们,我是微客客服,一个全能的在线客服小能手。今天,我要给大家秀一秀我的超能力——多渠道对接! 想象一下,你的企业就像一个繁忙的交通枢纽,客户咨询就像来来往往的车辆,而我,就是那个指挥交通的智能信号灯。我能帮你把客户咨询引导到正确的地方,让一切沟通都井…

Overleaf中插入pdf图片只显示图片路径的解决方式

最近在用Overleaf写一篇论文,使用IEEE的LaTex模板时发现一个问题,我使用pdfLaTex编译器无法正确显示我插入的pdf图片,网上翻解决方式没有翻到,误打误撞解决了这个问题,问题如下图所示: 即只在图片区域显示路径,不显示图片本身,解决方案是: 在右侧设置里找到编译模式,…