KMP是啥?KMP当然是KMPlayer的简称啦

KMP算法是用来解决字符串匹配的一种算法,由D.E.Knuth、J.H.Morris和V.R.Pratt同时发现,然后它可以用来干啥呢?我们上个例题:

给定两个字符串\(S,T\),问\(T\)在\(S\)中出现了多少次,出现的起始位置不同即为不同

\(O(n^2)\)暴力!(我当然知道你会)

\(|S|,|T|\leqslant 10^5\),怎么做?

所以这时我们就需要引入KMP算法,它能在最坏\(O(n)\)的复杂度下解决子串匹配的问题

首先我们考虑一下\(O(n^2)\)的冗余在哪里?举个栗子吧,我令S='aaaaaaaaac',T='aaaac',然后跑一遍\(O(n^2)\),你会发现每次\(S_{i+5}\)与\(T_5\)失配后,它会从\(S_{i+1}\)从头开始匹配,但是,其实没必要重新开始对吧?因此你发现这个\(O(n^2)\)的算法做了非常多没有意义的匹配,导致时间复杂度急剧增加,然后就TLE了

发现问题自然需要解决,如何解决?KMP算法就基于这个冗余提出了优化方案,它建立了一个对于任意字符串\(S\)而言的Next(C++11中next是关键字,所以我使用大写,并在之后简称为\(N\)数组)数组,\(N_i\)表示\([S_1...S_i]\)中前缀后缀相等的长度,也就是有\([S_1...S_{N_i}]=[S_{i-N_i+1}..S_i]\),举个栗子,若字符串S='abcabc',则它的\(N\)数组为\(\{0,0,0,1,2,3\}\)(\(N_1=0\)是定义的)

有了\(N\)数组后有何用?既然是为了解决冗余的,那我们就来看看它如何解决这个冗余。我们依然采用之前的栗子,首先对于\(T\)串求出其\(N\)数组:\(\{0,1,2,3,0\}\),然后我们进行匹配,然后遇到\(S_5\)与\(T_5\)失配,然后怎么处理?

当然把它俩从头开始啊(\(n^2\)了啊喂,你\(N\)白求了);你发现\(T_4\rightarrow T_5\)过程中与\(S_4\rightarrow S_5\)失配了,然后想想\(N\)数组的性质,可能存在\(T_{N_4}\rightarrow T_{N_4+1}\)能匹配啊,然后你就只需要把枚举\(T\)的指针疯狂跳\(N\)数组,直到能匹配为止

然后我们画个图来理解一下

这里红色平行线之间的完全相同的部分,之后就是失配的字符,绿色的便是后缀和前缀相同的部分

然后我们就将T往后挪一点点,黄色部分和绿色部分相同,然后黑色箭头则说明T中的位置在T'中对应的位置,棕色箭头即为跳\(N\)数组的过程

然后我们贴个代码

for (int i=1,j=0;i<=Lens;i++){
while (j&&s[i]!=t[j+1]) j=Next[j];
if (s[i]==t[j+1]) j++;
if (j==Lent) j=Next[j],Ans++;//就算匹配了也要跳一次匹配其他的,因为是统计出现次数
}

然后这题就做完了对吧?不对,我还没有讲\(N\)数组的构造方法……其实构造方法和匹配差不多,贴个代码,读者们可以自己看下

for (int i=2,j=0;i<=Lent;i++){
while (j&&t[i]!=t[j+1]) j=Next[j];
if (t[i]==t[j+1]) j++;
Next[i]=j;
}

然后我们来考虑一下时间复杂度,显然是\(O(n)\)的,做道例题吧

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

求\(T\)在\(S\)中的出现位置,并且输出\(T\)的Next数组

直接套用板子就好

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 0x7f7f7f7f
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
inline int read(){
int x=0,f=1;char ch=getchar();
for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
for (;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
return x*f;
}
inline void print(int x){
if (x>=10) print(x/10);
putchar(x%10+'0');
}
const int N=1e6;
char t[N+10],s[N+10];
int Next[N+10];
int Lens,Lent,ans;
void get(){
for (int i=2,j=0;i<=Lent;i++){
while (j&&t[i]!=t[j+1]) j=Next[j];
if (t[i]==t[j+1]) j++;
Next[i]=j;
}
}
void work(){
get();
for (int i=1,j=0;i<=Lens;i++){
while (j&&s[i]!=t[j+1]) j=Next[j];
if (s[i]==t[j+1]) j++;
if (j==Lent) j=Next[j],printf("%d\n",i-Lent+1);
}
}
int main(){
scanf("%s",s+1);
scanf("%s",t+1);
Lens=strlen(s+1),Lent=strlen(t+1),ans=0;
work();
for (int i=1;i<=Lent;i++) i!=Lent?printf("%d ",Next[i]):printf("%d\n",Next[i]);
return 0;
}

浅谈算法——KMP的更多相关文章

  1. 浅谈算法和数据结构: 七 二叉查找树 八 平衡查找树之2-3树 九 平衡查找树之红黑树 十 平衡查找树之B树

    http://www.cnblogs.com/yangecnu/p/Introduce-Binary-Search-Tree.html 前文介绍了符号表的两种实现,无序链表和有序数组,无序链表在插入的 ...

  2. 浅谈算法——AC自动机

    在学习AC自动机之前,你需要两个前置知识:Trie树,KMP 首先我们需要明白,AC自动机是干什么的(用来自动AC的) 大家都知道KMP算法是求单字符串对单字符串的匹配问题的,那么多字符在单字符上匹配 ...

  3. 浅谈算法和数据结构: 十 平衡查找树之B树

    前面讲解了平衡查找树中的2-3树以及其实现红黑树.2-3树种,一个节点最多有2个key,而红黑树则使用染色的方式来标识这两个key. 维基百科对B树的定义为“在计算机科学中,B树(B-tree)是一种 ...

  4. 转 浅谈算法和数据结构: 十 平衡查找树之B树

    前面讲解了平衡查找树中的2-3树以及其实现红黑树.2-3树种,一个节点最多有2个key,而红黑树则使用染色的方式来标识这两个key. 维基百科对B树的定义为"在计算机科学中,B树(B-tre ...

  5. 浅谈算法——线段树之Lazy标记

    一.前言 前面我们已经知道线段树能够进行单点修改和区间查询操作(基本线段树).那么如果需要修改的是一个区间该怎么办呢?如果是暴力修改到叶子节点,复杂度即为\(O(nlog n)\),显然是十分不优秀的 ...

  6. 浅谈算法——Manacher

    字符串算法在各大高级比赛中均有用到,所以,学习好字符串算法对我们而言十分重要.那么,今天我们就给大家介绍一个快速求回文串的算法,Manacher算法,我们也习惯性叫它马拉车算法. 一.引入 首先我们要 ...

  7. 浅谈算法——FWT(快速沃尔什变换)

    其实FWT我啥都不会,反正就是记一波结论,记住就好-- 具体证明的话,推荐博客:FWT快速沃尔什变换学习笔记 现有一些卷积,形如 \(C_k=\sum\limits_{i\lor j=k}A_i*B_ ...

  8. 浅谈算法——splay

    BST(二叉查找树)是个有意思的东西,种类巨TM多,然后我们今天不讲其他的,我们今天就讲splay 首先,如果你不知道Splay是啥,你也得知道BST是啥 如上图就是一棵优美的BST,它对于每个点保证 ...

  9. Kmp算法浅谈

    Kmp算法浅谈 一.Kmp算法思想 在主串和模式串进行匹配时,利用next数组不改变主串的匹配指针而是改变模式串的匹配指针,减少大量的重复匹配时间.在Kmp算法中,next数组的构建是整个Kmp算法的 ...

随机推荐

  1. mysql的DUPLICATE KEY

    经常遇到这样的情景,向一个表里插入一条数据,如果已经存在就更新一下,用程序实现麻烦而且在并发的时候可能会有问题,这时用mysql的DUPLICATE KEY 很方便 用法如下: INSERT INTO ...

  2. Record is locked by another user

    Oracle修改表中记录时出现record is locked by another user的问题 在操作表时没有commit,导致表被锁,只要执行下面两行语句,就可以了将行锁解锁了. Select ...

  3. code[VS] 1297 硬币

    题目描写叙述 Description 我们知道即使是同一种面值的硬币,它们的重量也有可能不一样,由于它受到很多因素的影响,包含制造工艺和流程上的.可是不论什么一种面值的硬币的重量总是处于某个特定范围之 ...

  4. 【腾讯bugly干货分享】精神哥手把手教你怎样智斗ANR

    上帝说要有ANR,于是Bugly就有了ANR上报.那么ANR究竟是什么? 近期非常多童鞋问起精神哥ANR的问题,那么这次就来聊一下,鸡爪怎么泡才好吃.噢不,是怎样高速定位ANR. ANR是什么 简单说 ...

  5. (29)java web的hibernate使用-crud的dao

    1, 做个简单的util public class HibernateUtils { private static SessionFactory sf; static { //加载主要的配置文件 sf ...

  6. vmware Ubuntu忘记登录密码

    VMware Workstation 上的Ubuntu 11.10 登录时,用户密码忘记了,以下是我对此问题解决的方法: 1)重新启动Ubtuntu 16 系统,同时长时间按住(或连续点击)esc键进 ...

  7. td 中设置超出宽度显示省略号失效

    td测试内容超出显示省略号时,结果没有显示省略号,而是一直往后显示,且超出了td大小,强行挤大了table. 原因是因为td默认不换行. 解决方法: 1.强制td换行. IE加上word-break: ...

  8. MySQL的IFNULL简单使用说明

    MySQL IFNULL函数简介 MySQL IFNULL函数是MySQL控制流函数之一,它接受两个参数,如果不是NULL,则返回第一个参数. 否则,IFNULL函数返回第二个参数. 两个参数可以是文 ...

  9. javascript 继承实现方法

    1. [代码][JavaScript]代码     //1.对象冒充//说明:构造函数使用this关键字给所有属性和方法赋值(即采用类声明的构造函数方式).因为构造函数只是一个函数,所以可使Class ...

  10. stl里面的空间适配器

    一般而言,如果频繁地向system heap申请和释放空间很小的内存空间块(小于128B的),就会对系统内存资源产生很多内存碎片(fragment)的问题,而C++的::operator new() ...