算法------(13)KMP

例题:(1)AcWing 831. KMP字符串 

        。。其实写完也不太理解。。随便写点吧

        KMP就是求next数组和运用next的数组的过程。相比传统匹配模式一次更新一单位距离的慢速方法,next数组可以让下表字符串一次更新n - next【n】个距离,加快了匹配速度。next【i】记录的是某字符串的前i个字符中前缀与后缀最长的匹配长度。

        对于模版中的j,我的理解是已经匹配了j个字符,因此next【i】还可以理解为对于已经匹配了i个字符的字符串来说,假如后续匹配不成立,则该字符串至少还有next【i】个字符是匹配的。

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5+10,M = 1e6+10;
int ne[N];
int n,m;
char p[N],s[M];
int main()
{cin >> n >> p+1 >> m >> s+1;ne[1] = 0;for(int i = 2,j = 0;i<=n;i++){while(j && p[i] != p[j+1]) j = ne[j];if(p[i] == p[j+1]) j++;ne[i] = j;}for(int i = 1,j = 0;i<=m;i++){while(j && s[i] != p[j+1]) j = ne[j];if(s[i] == p[j+1]) j++;if(j == n){printf("%d ",i - n);j = ne[j];}}return 0;
}

练习:(1) Leetcode 459 重复的子字符串

        。。。好难啊!!!!这是easy???

        我们可以证明 非空字符串s可由一个子串重复多次构成 当且仅当 把两个s连接起来且去掉前面后面的各一个字符,其中仍然存在s作为新字符串的子串。 

        从前往后推很显然,从后往前推需要一定证明。显然我不会,摘抄一下Leetcode的答案。

(2) P1470 [USACO2.3] 最长前缀 Longest Prefix

         其实与KMP无关但不知道为什么加了个KMP的标签。。。没做出来。。

        跟之前做过的一些题目有些类似。dp[i]的含义为[0,i-1]个字母可以分解,因此我们枚举每一个j,只要满足[0,j-1]时能分解且[j,i-1]也在P集合内即可。对于查询[j,i-1],由于题目说P集合内字符串长度<=10,因此我们对每一个i只需要枚举10个j即可,这样大大优化了时间。

        以及不知为什么本地跑不了。。。(问了朋友可能是文件输入输出的关系)

#include <iostream>
#include <unordered_set>
#include <string>
using namespace std;
const int N = 2e5+10;
bool dp[N];
unordered_set<string> res;
int main(int argc, char** argv) {string ss;while(cin >> ss){if(ss == ".") break;res.insert(ss);}string ans = "";while(cin >> ss){ans += ss;}dp[0] = true;int n = ans.length();for(int i = 1;i<=n;i++){for(int j = i;j>=max(i-10,0);j--){if(dp[j] && (res.find(ans.substr(j,i-j))!=res.end())){dp[i] = true;continue;}}}for(int i = n;i>=0;i--){if(dp[i]){printf("%d",i);break;}}return 0;
}

(3) P3435 [POI2006] OKR-Periods of Words

        太难了太难了。。。

        一个前缀的最大周期长度就是其长度减去其最小匹配长度,也就是其最短的共同前后缀的长度。 next数组求的是最大匹配长度,而不断递归求next数组得到的正是不为0的最小匹配长度。递归处理时可以把next数组直接更新为求得的最短长度,这样求取速度会更快。

#include <iostream>
using namespace std;
typedef long long ll;
const int N = 1000010;
char str[N];
int ne[N];
int main(int argc, char** argv) {int n;scanf("%d%s",&n,str+1);ne[1] = 0;for(int i = 2,j = 0;i<=n;i++){while(j && str[i] != str[j+1]) j = ne[j];if(str[i] == str[j+1]) j++;ne[i] = j;}ll ans = 0;for(int i = 2,j = 2;i<=n;i++,j = i){while(ne[j]) j = ne[j];if(ne[i]) ne[i] = j;ans += i-j;}printf("%lld",ans);return 0;
}

(4) Leetcode 面试题17.17 多次搜索

          对每一个smalls内的字符串求next数组,然后与big字符串进行kmp匹配。注意kmp的i返回的是字符串的最后一个字符下标。以及每一次匹配都要先清空next数组。

class Solution {int ne[1010];vector<vector<int>> res;
public:void next(string str){char x[1010];int n = str.length();for(int i = 1;i<=n;i++) x[i] = str[i-1]; for(int i = 2,j = 0;i<=n;i++){while(j && x[i] != x[j+1]) j = ne[j];if(x[i] == x[j+1]) j++;ne[i] = j;}}vector<int> get(string a,string b){char x[1010],y[1010];int n = a.length(),m = b.length();for(int i = 1;i<=n;i++) x[i] = a[i-1];for(int i = 1;i<=m;i++) y[i] = b[i-1];vector<int> ans;for(int i = 1,j = 0;i<=n;i++){while(j && x[i] != y[j+1]) j = ne[j];if(x[i] == y[j+1]) j++;if(j == m){ans.push_back(i-m);j = ne[j];}}return ans;}vector<vector<int>> multiSearch(string big, vector<string>& smalls) {int n = smalls.size();for(int i = 0;i<n;i++){if(smalls[i] == ""){res.push_back({});continue;}memset(ne,0,sizeof(ne));next(smalls[i]);res.push_back(get(big,smalls[i]));}return res;}
};

(5) Leetcode 3036 匹配模式数组的子数组数目

        做出来的第一个Hard题!虽然完全不是自己想出来的!!。。。

        把nums转化为跟pattern数组一样模式的长度为n-1的数组,此题便转化为求nums数组中有几个子数组为pattern数组,这样一来直接用kmp匹配即可。转化这一步还是挺难想到的。

class Solution {int x[1000100],y[1000100],ne[1000100];// x,y 1-n
public:int countMatchingSubarrays(vector<int>& nums, vector<int>& pattern) {int n = nums.size()-1,m = pattern.size();for(int i = 0;i<n;i++){if(nums[i] < nums[i+1]) x[i+1] = 1;else if(nums[i] == nums[i+1]) x[i+1] = 0;else x[i+1] = -1;} for(int i = 1;i<=m;i++){y[i] = pattern[i-1];} for(int i = 2,j = 0;i<=m;i++){while(j && y[i] != y[j+1]) j = ne[j];if(y[i] == y[j+1]) j++;ne[i] = j; }int ans = 0;for(int i = 1,j = 0;i<=n;i++){while(j && x[i] != y[j+1]) j = ne[j];if(x[i] == y[j+1]) j++;if(j == m){ans++;j = ne[j];}}return ans;}
};

(6) Leetcode 3037.在无限流中寻找模式

         做出的第二道Hard题,想出了匹配方式但是在优化上栽了!不过还算可以!

        由于是无限流,所以每次更新就匹配一次肯定会超时,因此用vector处理,由于next数组是确定的,因此每次不用从头匹配,只需要一个一个进行匹配即可。

/*** Definition for an infinite stream.* class InfiniteStream {* public:*     InfiniteStream(vector<int> bits);*     int next();* };*/
class Solution {int ne[110000];vector<int> p,q;
public:int findPattern(InfiniteStream* stream, vector<int>& pattern) {p.push_back(0);q.push_back(0);int r = 0,m = pattern.size();for(int i = 1;i<=m;i++){q.push_back(pattern[i-1]);}for(int i = 2,j = 0;i<=m;i++){while(j && q[i] != q[j+1]) j = ne[j];if(q[i] == q[j+1]) j++;ne[i] = j;}int i = 1,j = 0;while(1){p.push_back(stream->next());while(j && p[i] != q[j+1]) j = ne[j];if(p[i] == q[j+1]) j++;if(j == m) return i-m;i++;}}
};

(7) Leetcode 3008.找出数组中的美丽下标

         Hard被卡了。。

        匹配用kmp,后续查找时的优化思路为对a中每一个下标数组,在b的下标数组二分查找第一个大于等于它的数,如果这个数存在则返回其与a中对应下标的差,和其前面一个与a中对应下标的差,如果满足则返回。

class Solution {char p[500100],q[500100],r[500010];int ne1[500100],ne2[500100];
public:vector<int> beautifulIndices(string s, string a, string b, int k) {vector<int> res;vector<int> res1,res2;int n = a.length(),m = b.length(),o = s.length();for(int i = 1;i<=n;i++) p[i] = a[i-1];for(int i = 1;i<=m;i++) q[i] = b[i-1];for(int i = 1;i<=o;i++) r[i] = s[i-1];for(int i = 2,j = 0;i<=n;i++){while(j && p[i] != p[j+1]) j = ne1[j]; if(p[i] == p[j+1]) j++; ne1[i] = j;}for(int i = 2,j = 0;i<=m;i++){while(j && q[i] != q[j+1]) j = ne2[j]; if(q[i] == q[j+1]) j++; ne2[i] = j;}for(int i = 1,j = 0;i<=o;i++){while(j && r[i] != p[j+1]) j = ne1[j]; if(r[i] == p[j+1]) j++; if(j == n){res1.push_back(i-n);j = ne1[j];} }for(int i = 1,j = 0;i<=o;i++){while(j && r[i] != q[j+1]) j = ne2[j]; if(r[i] == q[j+1]) j++; if(j == m){res2.push_back(i-m);j = ne2[j];} }int c1 = res1.size(),c2 = res2.size();for (int i: res1) {auto it = lower_bound(res2.begin(), res2.end(), i);if (it != res2.end() && *it - i <= k ||it != res2.begin() && i - *--it <= k) {res.push_back(i);}}return res;}
};

(8) Leetcode 758.字符串中的加粗单词

         没啥难的。。挑错时间太长所以贴上来羞辱一下自己。。

        kmp加合并区间。首先先对words每一个字符串求next然后与s进行匹配,求出每一个需要加粗的区间。由于这些区间存在重叠而导致标签数过多,因此要求最小的标签数就需要合并区间。

class Solution {int ne[15];typedef pair<int,int> pii;
public:vector<pii> segs;vector<char> init(string x){vector<char> p;p.push_back(0);int n = x.length();for(int i = 1;i<=n;i++) p.push_back(x[i-1]);return p;}void next(vector<char> x){int n = x.size()-1;for(int i = 2,j = 0;i<=n;i++){while(j && x[i] != x[j+1]) j = ne[j];if(x[i] == x[j+1]) j++;ne[i] = j;}}void kmp(vector<char> x,vector<char> y){int n = x.size() - 1,m = y.size() - 1;for(int i = 1,j = 0;i<=n;i++){while(j && x[i] != y[j+1]) j = ne[j];if(x[i] == y[j+1]) j++;if(j == m){segs.push_back({i-m,i-1});j = ne[j];}}}string boldWords(vector<string>& words, string s) {int n = words.size();auto p = init(s);for(int i = 0;i<n;i++){memset(ne,0,sizeof(ne));auto q = init(words[i]);next(q);kmp(p,q);}vector<pii> res;sort(segs.begin(),segs.end());int fs = -2e9,ed = -2e9;for(auto seg:segs){int l = seg.first,r = seg.second;if(ed+1<l){if(fs!=-2e9) res.push_back({fs,ed});fs = l;ed = r;}else{ed = max(ed,r);}}if(fs!=-2e9) res.push_back({fs,ed});int flag = 0;string ans = "";for(auto seg:res){int l = seg.first,r = seg.second;ans += s.substr(flag,l - flag);ans += "<b>";ans += s.substr(l,r-l+1);ans += "</b>";flag = r+1;}n = s.length();cout <<flag;if(flag < n ) ans += s.substr(flag,n-flag);return ans;}
};

 (9) P2375 [NOI2014] 动物园           很难,虽然写出来了但并不完全理解。。

           在求取next数组的过程中,我们可以求取每一个i所对应的有相同的前后缀的所有子串的个数,然后再进行一次匹配,当我们递归到子串长度不重叠时,就可以得到该长度下所有的不重叠的子串有相同前后缀的个数。

#include <iostream>
#include <cstring>
using namespace std; 
const int N = 1e6+10,mod = 1e9+7;
typedef long long ll;
char p[N];
int ne[N],nums[N];
int main(int argc, char** argv) {int t;scanf("%d",&t);while(t--){scanf("%s",p+1);int n = strlen(p+1);nums[1] = 1;for(int i = 2,j = 0;i<=n;i++){while(j && p[i] != p[j+1]) j = ne[j];if(p[i] == p[j+1]) j++;ne[i] = j;nums[i] = nums[ne[i]] + 1;}ll res = 1;for(int i = 1,j = 0;i<=n;i++){while(j && p[i]!=p[j+1]) j = ne[j];if(p[i] == p[j+1]) j++;while(j * 2 > i) j = ne[j]; res = res * (nums[j] + 1) % mod;}printf("%lld\n",res);for(int i = 1;i<=n;i++){ne[i] = 0;nums[i] = 0;} }return 0;
}

 

        

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

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

相关文章

【研发日记】Matlab/Simulink技能解锁(三)——在Stateflow编辑窗口Debug

文章目录 前言 State断点 Transition断点 条件断点 按State步进 Watch Data Value Sequence Viewer 分析和应用 总结 前言 见《【研发日记】Matlab/Simulink技能解锁(一)——在Simulink编辑窗口Debug》 见《【研发日记】Matlab/Simulink技能解锁(二)——在Function编辑…

C++之类和对象(2)

目录 1.类的6个默认成员函数 2. 构造函数 2.1 概念 2.2 特性 3.析构函数 3.1 概念 3.2 特性 4. 拷贝构造函数 4.1 概念 4.2 特征 5.赋值运算符重载 5.1 运算符重载 5.2 赋值运算符重载 2. 赋值运算符只能重载成类的成员函数不能重载成全局函数 3. 用户没有显式实现时&…

Flink CDC 提取记录变更时间作为事件时间和 Hudi 表的 precombine.field 以及1970-01-01 取值问题

博主历时三年精心创作的《大数据平台架构与原型实现&#xff1a;数据中台建设实战》一书现已由知名IT图书品牌电子工业出版社博文视点出版发行&#xff0c;点击《重磅推荐&#xff1a;建大数据平台太难了&#xff01;给我发个工程原型吧&#xff01;》了解图书详情&#xff0c;…

深入剖析k8s-控制器思想

引言 本文是《深入剖析Kubernetes》学习笔记——《深入剖析Kubernetes》 正文 控制器都遵循K8s的项目中一个通用的编排模式——控制循环 for {实际状态 : 获取集群中对象X的实际状态期望状态 : 获取集群中对象X的期望状态if 实际状态 期望状态 {// do nothing} else {执行…

LeetCode 2120.执行所有后缀指令

现有一个 n x n 大小的网格&#xff0c;左上角单元格坐标 (0, 0) &#xff0c;右下角单元格坐标 (n - 1, n - 1) 。给你整数 n 和一个整数数组 startPos &#xff0c;其中 startPos [startrow, startcol] 表示机器人最开始在坐标为 (startrow, startcol) 的单元格上。 另给你…

多行业万能预约门店小程序源码系统 支持多门店预约小程序 带完整的安装代码包以及搭建教程

随着消费者对于服务体验要求的不断提升&#xff0c;门店预约系统成为了许多行业提升服务质量、提高运营效率的重要工具。然而&#xff0c;市面上的预约系统往往功能单一&#xff0c;无法满足多行业、多场景的个性化需求。下面&#xff0c;小编集合了多年的行业经验和技术积累&a…

11:日志分析系统ELK|Elasticsearch|kibana

日志分析系统ELK&#xff5c;Elasticsearch&#xff5c;kibana 日志分析系统ELKELK概述Elasticsearch安装Elasticsearch部署Elasticsearch集群Elasticsearch插件 熟悉Elasticsearch的API调用_cat API创建 tedu 索引使用 PUT 方式增加数据查询数据修改数据删除数据 KibanaKibana…

Android T 远程动画显示流程其三——桌面侧动画启动到系统侧结束流程

前言 接着前文分析Android T 远程动画显示流程其二 我们通过IRemoteAnimationRunner跨进程通信从系统进程来到了桌面进程&#xff0c;这里是真正动画播放的逻辑。 之后又通过IRemoteAnimationFinishedCallback跨进程通信回到系统进程&#xff0c;处理动画结束时的逻辑。 进入…

基于yolov5的电瓶车和自行车检测系统,可进行图像目标检测,也可进行视屏和摄像检测(pytorch框架)【python源码+UI界面+功能源码详解】

功能演示&#xff1a; 基于yolov5的电瓶车和自行车检测系统_哔哩哔哩_bilibili &#xff08;一&#xff09;简介 基于yolov5的电瓶车和自行车检测系统是在pytorch框架下实现的&#xff0c;这是一个完整的项目&#xff0c;包括代码&#xff0c;数据集&#xff0c;训练好的模型…

svn介绍 4.0

一、svn介绍&#xff08;版本控制工具&#xff09; 1、svn的定义&#xff1a; svn是一个开放源代码的版本控制系统&#xff0c;通过采用分支管理系统的高效管理&#xff0c;简而言之就是用于多个人共同开发同一个项目&#xff0c;实现共享资源&#xff0c;实现最终集中式个管…

2024年sCrypt编程马拉松即将开幕

BSV区块链的建设者们&#xff0c;你们在哪&#xff1f;2024年sCrypt编程马拉松即将拉开帷幕&#xff01; 2024年3月16日至17日&#xff0c;我们将在旧金山市举办一场以比特币智能合约&#xff08;即 sCrypt&#xff09;和比特币通证&#xff08;如Ordinals&#xff09;相结合为…

【蛀牙】日常生活如何正确护理牙齿?刷牙、洗牙、补牙

程序员生活指南之 【蛀牙】日常生活如何正确护理牙齿&#xff1f;刷牙、洗牙、补牙 文章目录 一、日常如何清洗牙齿&#xff1f;——刷牙与洗牙1、牙齿污垢1.1 牙菌斑1.2 软垢1.3 牙结石1.4 牙龈出血 2、如何刷牙2.1 关于时间2.2 各种工具2.3 巴氏刷牙法 二、定期进行洗牙3、如…

链表OJ刷题(二)

制作不易&#xff0c;三连支持一下呗&#xff01;&#xff01;&#xff01; 文章目录 前言一、链表的回文结构二、相交链表三、链表中倒数第k个节点四、环形链表Ⅰ和Ⅱ总结 前言 一、链表的回文结构 链表的回文结构_牛客题霸_牛客网 这里我们需要先了解一下什么叫做回文&#…

React之组件定义和事件处理

一、组件的分类 在react中&#xff0c;组件分为函数组件和class组件&#xff0c;也就是无状态组件和有状态组件。 * 更过时候我们应该区别使用无状态组件&#xff0c;因为如果有状态组件会触发生命周期所对应的一些函数 * 一旦触发他生命周期的函数&#xff0c;它就会影响当前项…

MQL5学习之RSI指标编写

研究MT5时发现MQL5这个指标编写功能很强大&#xff0c;应该是碾压国内所有的指标系统&#xff0c;不过这个东西相对复杂很多&#xff0c;比通达信公式不知复杂几许&#xff0c;看起来和C语法接近&#xff0c;倒是比较适合自己。试着玩一下&#xff0c;发现还是有点难度的。索性…

修改centos7的dns解决docker拉取镜像超时问题

近期在一台centos7的服务器上部署系统&#xff0c;拉取docker镜像时总是超时&#xff0c;如图所示。网上有教程说&#xff0c;可以修改操纵系统的dns地址&#xff0c;试了一下&#xff0c;果然搞定。 打开dns配置文件 sudo vi /etc/resolv.conf发觉里面的地址设为114.114.114…

Qt篇——QTableWidget保存表格数据到Excel文件中,读Excel内容到QTableWidget

表格和excel例子如下图所示&#xff1a; 一、QTableWidget保存表格数据到Excel文件中 代码如下&#xff1a; &#xff08;pro文件中添加QT axcontainer&#xff09; #include <QAxObject>void MainWindow::saveTableToExcel() {QDateTime current_date_time QDateTi…

交友社交软件开发-php交友聊天系统-

为了开发一个高效的交友系统&#xff0c;需要一个完善的信息管理和筛选机制。这个系统应该能够根据用户的个人信息、兴趣爱好、价值观等标准进行筛选&#xff0c;并向用户提供符合他们要求心仪的人的信息。为了实现这个目标&#xff0c;系统可以利用人工智能技术&#xff0c;分…

经销商文件分发 怎样兼顾安全和效率?

经销商文件分发是指将文件、资料、产品信息等从制造商或经销商传递给经销商的过程。这一过程对于确保经销商能够获取最新的产品信息、销售策略、市场活动资料等至关重要。 想要管理众多经销商合作伙伴之间的文件传输并提高效率&#xff0c;可以采取以下措施&#xff1a; 1、建…

中文文本分类(pytorch 实现)

import torch import torch.nn as nn import torchvision from torchvision import transforms, datasets import os, PIL, pathlib, warningswarnings.filterwarnings("ignore") # 忽略警告信息# win10系统 device torch.device("cuda" if torch.cuda.i…