字符串算法都是毒瘤的

一.kmp算法的用处

在文本串中查找模式串的位置,数量

文本串:要在这个字符串查找模式串

模式串:在文本串中查找的字符串

全是废话

二.kmp算法的思想

话说kmp好像是3个发明者的首字母

如果暴力在文本串中查找模式串,时间期望复杂度是O(N+M),N,M为文本串,模式串的长度,但经过毒瘤出题人的构造数据,暴力会被卡成O(NM)

kmp的精髓在于每次失配时,珂以不用从头开始

这样说有点迷,我们结合数据来看一下:

模式串:abcab
文本串:abcacababcab

首先前4位都匹配成功,但是,第5位出现了不同

这时,我们不要把模式串往右移1位,而是要移3位

模式串:   abcab
文本串:abcacababcab

但有时候不止1位重复,而是有好几个字符相同

模式串:abcabc
文本串:abcabdababcabc

匹配到第6位时失配了,我们珂以把模式串往右移3位

模式串:   abcabc
文本串:abcabdababcabc

那么现在已经很明了了, kmp匹配的重头戏就在于用失配数组来确定当某一位失配时,我们可以将前一位跳跃到之前匹配过的某一位。而此处有几个先决条件需要理解:

1.我们的失配数组应当建立在模式串意义下,而不是文本串意义下。因为显然模式串要更加灵活,在失配后换位时,更灵活简便地处理。

2.如何确定位置

首先我们要明白,基于先决条件1而言,我们在预处理时应当考虑当模式串的第 i 位失配时,应当跳转到哪里.因为在文本串中,之前匹配过的所有字符已经没有用了——都是匹配完成或者已经失配的,所以我们的 kmp 数组(即是用于确定失配后变化位置的数组,下同)应当记录的是:

在模式串 str1 中,对于每一位 str1(i) ,它的kmp数组应当是记录一个位置 j, j≤i

并且满足 str1(i)=str1(j) 并且在 j!=1时理应满足 str1(1)至str1(j-1) 分别与 str(i-j+1)~str1(i-1) 按位相等

3.从前缀后缀来解释 kmp匹配 :

首先解释前后缀(因为太简单就不解释了 qaq):

给定串:ABCABA
前缀:A,AB,ABC,ABCA,ABCAB,ABCABA
后缀:A,BA,ABA,CABA,BCABA,ABCABA

其实刚才的移位法则就是对于模式串的每个前缀而言,用kmp数组记录到它为止的模式串前缀的真前缀和真后缀最大相同的位置(注意,这个地方没有写错,是真的有嵌套 qaq )。然而这个地方我们要考虑“模式串前缀的前缀和后缀最大相同的位置”原因在于,我们需要用到kmp数组换位时,当且仅当未完全匹配。所以我们的操作只是针对模式串的前缀——毕竟是失配函数,失配之后只有可能是某个部分前缀需要“快速移动”。所以这就可以解释kmp匹配中前后缀应用的一个特点:

kmp匹配中前后缀不包括模式串本身,即只考虑真前缀和真后缀,因为模式串本身需要整体考虑,当且仅当匹配完整个串之后;而匹配完整个串不就完成匹配了吗 qaq

三.kmp匹配的代码实现

以下讲解都以Luogu P3375 【模板】KMP字符串匹配为例

1.kmp[i] 用于记录当匹配到模式串的第 i 位之后失配,该跳转到模式串的哪个位置,那么对于模式串的第一位和第二位而言,只能回跳到 1,因为是kmp匹配是要将真前缀跳跃到与它相同的真后缀上去(通常也可以反着理解),所以当 i=0或者 i=1时,相同的真前缀只会是 str1(0)这一个字符,所以 kmp[0]=kmp[1]=1。

2.算出kmp数组之后匹配就比较简单

k=0;
//k可以看做表示当前已经匹配完的模式串的最后一位的位置
//如果楼上看不懂,你也可以理解为j表示模式串匹配到第几位了
for(register int i=0;i<len1;++i)
{
while(k&&a1[i]!=a2[k])
k=kmp[k];
//如果失配 ,那么就不断向回跳,直到可以继续匹配
k+=a1[i]==a2[k]?1:0;
//如果匹配成功,那么对应的模式串位置向后移
if(k==len2)
printf("%d\n",i-len2+2),k=kmp[k];
//匹配成功,继续匹配
}

3.如何求kmp数组,我们用模式串自己匹配自己

int k=0;
for(register int i=1;i<len2;++i)
{
while(k&&a2[i]!=a2[k])
k=kmp[k];
//此处判断j是否为0的原因在于,如果回跳到第一个字符就不 用再回跳了
kmp[i+1]=a2[i]==a2[k]?++k:0;
}

完整代码,很简短,但其内涵还是需要细细理解

#include <bits/stdc++.h>
#define N 2000005
using namespace std;
char a1[N],a2[N];
int kmp[N];
int main()
{
scanf("%s%s",a1,a2);
kmp[0]=kmp[1]=0;
int len1=strlen(a1),len2=strlen(a2);
int k=0;
for(register int i=1;i<len2;++i)
{
while(k&&a2[i]!=a2[k])
k=kmp[k];
kmp[i+1]=a2[i]==a2[k]?++k:0;
}
k=0;
for(register int i=0;i<len1;++i)
{
while(k&&a1[i]!=a2[k])
k=kmp[k];
k+=a1[i]==a2[k]?1:0;
if(k==len2)
printf("%d\n",i-len2+2),k=kmp[k];
}
for(register int i=1;i<=len2;++i)
printf("%d ",kmp[i]);
return 0;
}

相关题目

1.Luogu P4391 [BOI2009]Radio Transmission 无线传输

详细题解

kmp匹配详解的更多相关文章

  1. kmp算法详解

    转自:http://blog.csdn.net/ddupd/article/details/19899263 KMP算法详解 KMP算法简介: KMP算法是一种高效的字符串匹配算法,关于字符串匹配最简 ...

  2. [转] KMP算法详解

    转载自:http://www.matrix67.com/blog/archives/115 KMP算法详解 如果机房马上要关门了,或者你急着要和MM约会,请直接跳到第六个自然段.    我们这里说的K ...

  3. KMP算法详解(转自中学生OI写的。。ORZ!)

    KMP算法详解 如果机房马上要关门了,或者你急着要和MM约会,请直接跳到第六个自然段. 我们这里说的KMP不是拿来放电影的(虽然我很喜欢这个软件),而是一种算法.KMP算法是拿来处理字符串匹配的.换句 ...

  4. 算法进阶面试题01——KMP算法详解、输出含两次原子串的最短串、判断T1是否包含T2子树、Manacher算法详解、使字符串成为最短回文串

    1.KMP算法详解与应用 子序列:可以连续可以不连续. 子数组/串:要连续 暴力方法:逐个位置比对. KMP:让前面的,指导后面. 概念建设: d的最长前缀与最长后缀的匹配长度为3.(前缀不能到最后一 ...

  5. 数据结构4.3_字符串模式匹配——KMP算法详解

    next数组表示字符串前后缀匹配的最大长度.是KMP算法的精髓所在.可以起到决定模式字符串右移多少长度以达到跳跃式匹配的高效模式. 以下是对next数组的解释: 如何求next数组: 相关链接:按顺序 ...

  6. KMP算法详解&&P3375 【模板】KMP字符串匹配题解

    KMP算法详解: KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt(雾)提出的. 对于字符串匹配问题(such as 问你在abababb中有多少个 ...

  7. 字符串匹配KMP算法详解

    1. 引言 以前看过很多次KMP算法,一直觉得很有用,但都没有搞明白,一方面是网上很少有比较详细的通俗易懂的讲解,另一方面也怪自己没有沉下心来研究.最近在leetcode上又遇见字符串匹配的题目,以此 ...

  8. KMP算法详解 --从july那学的

    KMP代码: int KmpSearch(char* s, char* p) { ; ; int sLen = strlen(s); int pLen = strlen(p); while (i &l ...

  9. 模式匹配的KMP算法详解

    这种由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现的改进的模式匹配算法简称为KMP算法.大概学过信息学的都知道,是个比较难理解的算法,今天特把它搞个彻彻底底明明白白. 注意到这 ...

随机推荐

  1. Spark Distributed matrix 分布式矩阵

    RowMatrix行矩阵 import org.apache.spark.rdd.RDD import org.apache.spark.mllib.linalg.Vectors import org ...

  2. 非常实用的使用eclipse的快捷键和技巧

    解决代码的自动提示问题: 1.打开 Eclipse -> Window -> Perferences 2.找到Java 下的 Editor 下的 Content Assist , 右边出现 ...

  3. 文文---Set,Map

    Set和Map Set:类似数组 成员的值是唯一的 下有 add(),has(),delete(),clear(),size 等方法 Map:类似于对象 成员的值是唯一的 下有 set(),has() ...

  4. vue-cli+webpack+router+vuex---之vuex使用

    有信心的可以去看官方的文档 vue 的官方文档有个显著的特点---代码粘贴不全 Vue中文站:cn.vuejs.org vue-router官方教程:router.vuejs.org/zh-cn vu ...

  5. 2018秋寒假作业4—PTA编程总结1

    7-1 打印沙漏 (20 分) 本题要求你写个程序把给定的符号打印成沙漏的形状.例如给定17个"*",要求按下列格式打印 所谓"沙漏形状",是指每行输出奇数个符 ...

  6. C#中的double类型数据向SQL sqerver 存储与读取问题

    1.存储 由于double类型在SQLsever中并没有对应数据,试过对应float.real类型,发现小数位都存在四舍五入的现象,目前我使用的是decimal类型,用此类型时个人觉得小数位数应该比自 ...

  7. Spring Boot事务管理(下)

    在上两篇 Spring Boot事务管理(上)和Spring Boot事务管理(中)的基础上介绍注解@Transactional. 5 @Transactional属性 属性 类型 描述 value ...

  8. Linux下的文件系统2

    2017-03-13 上文针对VFS的基本信息做了介绍,并简单介绍了VFS涉及的几个数据机构,本节结合LInux源码,对各个结构之间的关系进行分析. 一.总体架构图 总体架构图如上图所示,结合进程访问 ...

  9. 13.0-uC/OS-III上下文切换

    1.当uC/OS-III转向执行另一个任务的时候,它保存了当前任务的CPU寄存器到堆栈.并从新任务的的堆栈中CPU寄存器载入CPU.这个过程叫做上下文切换. 上下文切换需要一些开支. CPU的寄存器越 ...

  10. redis发布订阅、事务、脚本

    Redis 发布订阅 Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息. Redis 客户端可以订阅任意数量的频道. 下图展示了频道 cha ...