KMP算法

对于串s[1..n],我们定义fail[i]表示以串s[1..i]的最长公共真前后缀

我们首先考虑对于模式串p,如何计算出它的fail数组。定义fail[0]=-1。

  • 根据“真前后缀”的条件,容易得到fail[1]=0
  • 对于任意的i>1,显然在s[fail[i-1]+1]==s[i]的时候,我们有fail[i]=fail[i-1]
  • 如果s[fail[i-1]+1]!=s[i],那么我们需要去尝试s[fail[fail[i-1]]+1]是否与s[i]相等,否则再继续下去。

这是因为我们最终要找的是s[1..i]的最长公共前后缀,如果它不是由s[1..i-1]的最长公共前后缀扩增而来,那么它一定是由s[1..fail[i-1]]的最长公共前后缀扩增而来。毕竟,最长公共前后缀包含了若干个公共前后缀。即s[1..fail[i-1]]的最长公共前后缀也是s[1..i-1]的一个公共前后缀,虽然它未必最长。

// Build fail array for pattern string
for(int i=;i<=m;i++) {
fail[i]=fail[i-];
while(p[fail[i]+]-p[i] && fail[i]) fail[i]=fail[fail[i]];
if(p[fail[i]+]==p[i]) ++fail[i];
}
  fail[0]=-1;

预处理完毕后,匹配过程与其大同小异。

我们需要定义两个指针i,j,指向当前希望要匹配的两个字符。

如果s[i]!=p[j],则需要将模式串向右滑移,即j=fail[j-1]+1。当然如果j=0了,则重设j=1,并将模式串起点向右移动一位。

如果s[i]==p[j],当模式串没有匹配完全即j<m时,将两个指针同时向右移动即可。如果匹配完了,则报告一个结果,并将模式串按j=fail[j-1]+1进行滑移,以便于进行下一次匹配。

for(int i=,j=;i<=n;) {
if(s[i]==p[j]) {
if(j<m) ++i,++j;
else {
cout<<i-m+<<endl;
j=fail[j-]+;
}
}
else {
j=fail[j-]+;
if(j<=) j=,i++;
}
}

算法总体复杂度O(n+m)。

完整模板:

#include <bits/stdc++.h>
using namespace std; char s[],p[];
int n,m,fail[]; int main() {
ios::sync_with_stdio(false);
cin>>s+>>p+;
n=strlen(s+); m=strlen(p+);
// Build fail array for pattern string
for(int i=;i<=m;i++) {
fail[i]=fail[i-];
while(p[fail[i]+]-p[i] && fail[i]) fail[i]=fail[fail[i]];
if(p[fail[i]+]==p[i]) ++fail[i];
} fail[]=-;
for(int i=,j=;i<=n;) {
if(s[i]==p[j]) {
if(j<m) ++i,++j;
else {
cout<<i-m+<<endl;
j=fail[j-]+;
}
}
else {
j=fail[j-]+;
if(j<=) j=,i++;
}
}
for(int i=;i<=m;i++) cout<<(fail[i]<?:fail[i])<<" ";
cout<<endl;
}

#10046. 「一本通 2.2 练习 2」OKR-Periods of Words

容易发现这里我们要的其实是每个前缀减去最短的Border。类似并查集一样路径压缩一下即可。需要注意一些细节防止死循环。

#include <bits/stdc++.h>
using namespace std; char s[], p[];
int n, m, fail[]; int main() {
ios::sync_with_stdio(false);
cin >> m;
cin >> p + ;
// Build fail array for pattern string
long long ans = ;
fail[] = ;
for (int i = ; i <= m; i++) {
fail[i] = fail[i - ];
while (p[fail[i] + ] - p[i] && fail[i]) fail[i] = fail[fail[i]];
if (p[fail[i] + ] == p[i])
++fail[i];
}
fail[] = -;
for (int i = ; i <= m; i++) {
int j = i;
while (fail[j] > ) j = fail[j];
ans += i - j;
if (fail[i])
fail[i] = j;
}
cout << ans << endl;
}

#10047. 「一本通 2.2 练习 3」似乎在梦中见过的样子

#include <bits/stdc++.h>
using namespace std; char s[],p[];
int n,m,k,fail[],f[];
long long ans = ; int main() {
ios::sync_with_stdio(false);
cin>>p+>>k;
m=strlen(p+);
while(m) {
for(int i=;i<=m;i++) {
fail[i]=fail[i-];
while(p[fail[i]+]-p[i] && fail[i]) fail[i]=fail[fail[i]];
if(p[fail[i]+]==p[i]) ++fail[i];
if(f[fail[i]]) f[i]=f[fail[i]];
else if(fail[i]>=k) f[i]=fail[i];
else f[i]=;
if(f[i] && (f[i]*)<i) ++ans;
}
--m;
for(int i=;i<=m;i++) p[i]=p[i+];
memset(fail,,sizeof fail);
memset(f,,sizeof f);
}
cout<<ans<<endl;
}

#10048. 「一本通 2.2 练习 4」Censoring

搞个栈记录输出,顺便存下每个被输出点匹配到模式串的哪个位置

这样如果匹配就弹掉栈顶几个,并读取新栈顶原先匹配到了哪个位置,接下去匹配即可

#include <bits/stdc++.h>
using namespace std; char s[],p[];
int n,m,fail[],sta[],staj[],top; int main() {
ios::sync_with_stdio(false);
cin>>s+>>p+; n=strlen(s+); m=strlen(p+);
for(int i=;i<=m;i++) {
fail[i]=fail[i-];
while(p[fail[i]+]-p[i] && fail[i]) fail[i]=fail[fail[i]];
if(p[fail[i]+]==p[i]) ++fail[i];
} fail[]=-;
for(int i=,j=;i<=n;) {
if(s[i]==p[j]) {
if(j<m) ++top,sta[top]=i,staj[top]=j,++i,++j;
else top-=m-,j=staj[top]+,++i;
}
else {
j=fail[j-]+;
if(j<=) ++top,sta[top]=i,staj[top]=j,i++;
}
}
for(int i=;i<=top;i++) cout<<s[sta[i]];
cout<<endl;
}

#include <bits/stdc++.h>using namespace std;
char s[100005],p[100005];int n,m,k,fail[100005],f[100005];long long ans = 0;
int main() {ios::sync_with_stdio(false);cin>>p+1>>k;m=strlen(p+1);while(m) {        for(int i=2;i<=m;i++) {            fail[i]=fail[i-1];            while(p[fail[i]+1]-p[i] && fail[i]) fail[i]=fail[fail[i]];            if(p[fail[i]+1]==p[i]) ++fail[i];            if(f[fail[i]]) f[i]=f[fail[i]];            else if(fail[i]>=k) f[i]=fail[i];            else f[i]=0;            if(f[i] && (f[i]*2)<i) ++ans;        }        --m;        for(int i=1;i<=m;i++) p[i]=p[i+1];        memset(fail,0,sizeof fail);        memset(f,0,sizeof f);}cout<<ans<<endl;}

[一本通学习笔记] KMP算法的更多相关文章

  1. 学习笔记-KMP算法

    按照学习计划和TimeMachine学长的推荐,学习了一下KMP算法. 昨晚晚自习下课前粗略的看了看,发现根本理解不了高端的next数组啊有木有,不过好在在今天系统的学习了之后感觉是有很大提升的了,起 ...

  2. [ML学习笔记] XGBoost算法

    [ML学习笔记] XGBoost算法 回归树 决策树可用于分类和回归,分类的结果是离散值(类别),回归的结果是连续值(数值),但本质都是特征(feature)到结果/标签(label)之间的映射. 这 ...

  3. 学习笔记 - Manacher算法

    Manacher算法 - 学习笔记 是从最近Codeforces的一场比赛了解到这个算法的~ 非常新奇,毕竟是第一次听说 \(O(n)\) 的回文串算法 我在 vjudge 上开了一个[练习],有兴趣 ...

  4. 算法笔记--KMP算法 && EXKMP算法

    1.KMP算法 这个博客写的不错:http://www.cnblogs.com/SYCstudio/p/7194315.html 模板: next数组的求解,那个循环本质就是如果相同前后缀不能加上该位 ...

  5. 来去学习之---KMP算法--next计算过程

    一.概述 KMP算法是一种字符串匹配算法,比如现有字符串 T:ABCDABCDABCDCABCDABCDE, P:ABCDABCDE P字符串对应的next值:[0,0,0,0,1,2,3,4,0] ...

  6. 学习笔记——EM算法

    EM算法是一种迭代算法,用于含有隐变量(hidden variable)的概率模型参数的极大似然估计,或极大后验概率估计.EM算法的每次迭代由两步组成:E步,求期望(expectation):M步,求 ...

  7. 数据挖掘学习笔记--AdaBoost算法(一)

    声明: 这篇笔记是自己对AdaBoost原理的一些理解,如果有错,还望指正,俯谢- 背景: AdaBoost算法,这个算法思路简单,但是论文真是各种晦涩啊-,以下是自己看了A Short Introd ...

  8. [一本通学习笔记] AC自动机

    AC自动机可以看作是在Trie树上建立了fail指针,在这里可以看作fail链.如果u的fail链指向v,那么v的对应串一定是u对应串在所给定字符串集合的后缀集合中的最长的后缀. 我们考虑一下如何实现 ...

  9. Java学习笔记——排序算法之快速排序

    会当凌绝顶,一览众山小. --望岳 如果说有哪个排序算法不能不会,那就是快速排序(Quick Sort)了 快速排序简单而高效,是最适合学习的进阶排序算法. 直接上代码: public class Q ...

随机推荐

  1. 通过shell模拟redis-trib.rb info的输出

    需求:模拟redis-trib.rb info ip:port输出的结果 如: [redis@lxd-vm3 ~]$ redis-trib.rb info 5.5.5.101:29001 /usr/l ...

  2. awk - 提取包含某个关键字的段落

    前提 AWK是一种处理文本文件的语言,是一个强大的文本分析工具. 本文将使用命令awk将具有某个关键字的段落提取出来. 准备数据 "Finalizer" #3 daemon pri ...

  3. maven的核心概念——创建war工程

    第十七章第四个Maven工程(war工程) 17.1 创建步骤 ①第一步:创建maven web工程 ②第二步:修改web.xml <?xml version="1.0" e ...

  4. 清北学堂—2020.1提高储备营—Day 1 afternoon(二分、分治、贪心)

    qbxt Day 1 afternoon --2020.1.17 济南 主讲:李佳实 目录一览 1.二分法 2.分治 3.贪心 总知识点:基础算法 一.二分法 (1)算法分析:二分法是一种暴力枚举的优 ...

  5. Codeforces Round #622(Div 2) C1. Skyscrapers (easy version)

    题目链接: C1. Skyscrapers (easy version) 题目描述: 有一行数,使得整个序列满足 先递增在递减(或者只递增,或者只递减) ,每个位置上的数可以改变,但是最大不能超过原来 ...

  6. Ansible-Tower使用文档

    导航栏介绍 # viewes Dashboard 仪表盘展示信息的 Jobs 跑过的任务记录 Schedules 计划任务 My View 查看用户的工作模版,和任务记录 # resources Te ...

  7. linux基础之IO模型

    一.IO模型 一次read操作: (1)等待数据准备好:从磁盘到内核内存 (2)从内核内存复制到进程内存 示意图如下: I/O类型: 同步和异步:synchronous,asynchronous 关注 ...

  8. idea 代码没有被svn控制

    背景 开发从svn上拉下来的代码,上传时发现idea的快捷键(ctrl+T)没反应以及菜单栏中没有相关按钮. 原因 发现项目当前文件夹里没有 .svn 隐藏文件夹,所以当前文件夹就没有被idea识别继 ...

  9. mongo操作图片储存

    python 将图片存入mongodb,读取图片,gridfs模块原创A873054267 最后发布于2018-11-06 15:49:30 阅读数 2785 收藏展开导入图片引入模块,其中gridf ...

  10. Conference deadlines

    1. NLP/IR/DM/ML Conference Deadlines(Updating) Two Principles of Deadlines:1. All deadlines converge ...