当前位置: 首页 > news >正文

洛谷P3375 【模板】KMP字符串匹配

洛谷P3375 【模板】KMP字符串匹配
题目大意
给定两个字符串 s1s1 和 s2s2,要求输出 s2s2 在 s1s1 中所有出现的位置(起始下标),并输出 s2s2 的每个前缀的最长 border 长度。
数据范围: ∣s1∣,∣s2∣≤1e6∣s1∣,∣s2∣≤1e6 。

一般思路
我们很容易想到的就是匹配字符串时,我们从目标字符串 长度为 n 的 s1 的第一个下标选取和长度为 m 的 s2 长度一样的子字符串进行比较,如果一样,就返回开始处的下标值,不一样,选取 s1 下一个下标,同样从 s2 选取长度为 n 的字符串进行比较,直到 s1 的末尾。
显然,我们发现了许多不合理的操作:比对失败时会从头开始匹配,浪费了许多时间,时间复杂度为 O(nm)。

分析
问题为经典的多模式串匹配问题,需要在线性时间内完成。
KMP(Knuth-Morris-Pratt)算法通过预处理模式串的 next 数组(即每个前缀的最长 border),在匹配时利用已匹配信息避免回溯,达到 O(∣s1∣+∣s2∣)O(∣s1∣+∣s2∣) 的时间复杂度。
border 定义:对于一个字符串 ss,它的一个非本身的前缀同时也是后缀,称为 border。最长 border 即最长的这样的前缀(也是后缀)。
然后我们就可以借助border进行跳跃,不需要再从头匹配
Solution
标签: 字符串,KMP
算法思路

  1. 预处理 next 数组(代码中为 kmp 数组):
    对于模式串 s2(下标从 1 开始),kmp[i] 表示长度为 i 的前缀的最长 border 长度。
    通过双指针 i 和 j 递推得到:
    初始化 j = 0,kmp[1] = 0(长度为 1 的前缀无 border)。
    对于 i = 2 到 len2,当 j > 0 且当前字符不匹配时,j = kmp[j] 回退;若匹配则 j++,然后 kmp[i] = j。
    2. 匹配过程:
    在文本串 s1s1 上同样用双指针 i 和 j 进行匹配。
    i 遍历文本串,j 表示当前已匹配的模式串长度。
    当字符不匹配时,j = kmp[j] 回退。
    若匹配则 j++,当 j == len2 时说明找到一个匹配,输出起始位置 i - len2 + 1,然后 j = kmp[j] 继续寻找下一个匹配。
    3. 输出结果:
    先输出所有匹配位置(每行一个),最后输出一行 len2 个整数,即 kmp[1] 到 kmp[len2]。
    代码说明
    为方便处理,在字符串前加了一个空格,使得下标从 1 开始,避免下标转换。
    vector kmp(len2 + 1) 存储 next 数组。
    预处理和匹配过程均使用 while 循环处理失配回退。
    注意输出格式:匹配位置每行一个,最后一行 border 长度用空格隔开。
    时间复杂度
    预处理 O(∣s2∣)O(∣s2∣),匹配 O(∣s1∣)O(∣s1∣),总复杂度线性,可处理大规模数据。

下面是代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define endl '\n'
#define fast ios::sync_with_stdio(0),cin.tie(0)
#define pll pair<ll,ll>void solve(){string s1,s2;cin >> s1 >> s2;s1 = " " + s1;s2 = " " + s2;ll len1 = s1.size() - 1;ll len2 = s2.size() - 1;vector<ll> kmp(len2 + 1);for(ll i = 2, j = 0; i <= len2; i++){while(j && s2[i] != s2[j + 1]) j = kmp[j];if(s2[i] == s2[j + 1]) j++;kmp[i] = j;}for(ll i = 1, j = 0; i <= len1; i++){while(j && s1[i] != s2[j + 1]) j = kmp[j];//可以的话就过了,否则会变成0if(s1[i] == s2[j + 1]) j++;if(j == len2){cout<<i - len2 + 1<<endl;j = kmp[j];}}for(ll i = 1; i <= len2; i++){cout<<kmp[i]<<" ";}return;
}int main(){fast;ll t = 1; //cin >> t;while(t--){solve();}return 0;
}  
http://www.jsqmd.com/news/415527/

相关文章:

  • B002 排序 双指针 哈希表 两数之和到K数之和 1640~1642 CSES
  • 110kV三段式相间距离保护参数整定计算设计simulink仿真
  • 【每日一题】LeetCode 1404. 将二进制表示减到 1 的步骤数
  • 【村儿网通】把 Scaled Dot-Product Attention 展开写一遍
  • Andrew Stankevich Contest 44 (ASC 44) 总结
  • nohup ./webserver
  • 基于Lyapunov的控制器设计用于自主水下车辆(AUV)的轨迹跟踪,对于欠驱动的自主水下车辆(AUV)进行二维轨迹跟踪的仿真Lyapunov控制器设计附Simulink仿真、Matlab代码
  • 基于LSTM和SVM的设备故障诊断附Matlab代码
  • C++中的友元 之七
  • CT断层成像系列10——三维锥束FDK重建算法(附Matlab代码)
  • 东方博宜OJ 1108:正整数N转换成一个二进制数 ← 字符串 / 栈
  • 渗透测试零基础入门!从环境搭建到实战靶场通关,一篇吃透
  • 【渗透测试】一文吃透SQL注入漏洞!原理+分类+实战利用+防御方案
  • 260204
  • 【Playwright 】端到端自动化的开源框架
  • 【matlab】GUI句柄
  • 专业的文件上传漏洞检测工具,支持263+绕过技术、代理抓包、动态扫描
  • C++中的友元 之六
  • 五款免费AI视频生成神器,效果炸裂!
  • STM32F103C8T6 驱动 180° 舵机(SG90)超详细教程
  • 【开题答辩全过程】以 共享单车使用情况预测模型的设计与实现为例,包含答辩的问题和答案
  • C++中的友元 之五
  • 互斥锁
  • 数据库的应用-第一天
  • P3035 [USACO11DEC] Umbrellas for Cows S 题解
  • AI Compose Commit:用 AI 智能重构 Git 提交工作流
  • 题解:P11567 建造军营 II
  • C++中的友元 之四
  • 哈萨克斯坦旅游出行笔记
  • 2026年广州名士表手表维修推荐榜单评测:非官方维修网点服务与售后中心选择指南 - 十大品牌推荐