【例题传送门:caioj1177


KMP模版:子串是否出现

【题意】
有两个字符串SA和SB,SA是母串,SB是子串,问子串SB是否在母串SA中出现过。
如果出现过输出第一次出现的起始位置和结束位置,否则输出"NO"
【输入文件】
第一行SA(1<= 长度<=1000000)
第二行SB(1<= 长度<=1000)
【输出文件】
如果SB在SA中出现过输出第一次出现的起始位置和结束位置,否则输出"NO"
【样例1输入】
aaaaabaa
aab
【样例1输出】
4 6
【样例2输入】
aaaaabaa
aax
【样例2输出】
NO


算法分析:

  KMP其实就是一种偷懒的方式,通过访问前面已经遍历过的位置来首先给定一个可继承的值,然后暴力(汗......)

  首先定义一个p数组,p[i]=j时,代表SA字符串[1...j]与SA字符串[i-j+1...i]所形成的子串完全相同

  那么我们怎么得出这个p数组呢?

  就是暴力+偷懒!!!(其实前面已经讲了,尴尬)

  当我们访问到i这个点时,我们先访问一下i-1,因为我们访问到i的时候已经把1~i-1的p数组求出来了,那么请看下面的图:

  我们设j=p[i-1],i-1-j+1=i-j,那么我们可以知道SA[1...j]=SA[i-j...i-1],也就是上图的红色区间,那假如SA[j+1]=SA[i]的话,我们就可以直接p[i]=j+1对吧

  但是现实是残酷的(AC没有那么容易~),那么当SA[j+1]!=SA[i]的时候怎么办呢?那么我们就找p[j]!!!如图:

  我们再设一个变量k=p[j],因为p[j]的定义,所以我们可以得到SA[1...k]=SA[j-k+1...j],也就是前两个棕色区间,这时因为两个红色区间相同,所以我们可以得到SA[1...k]=SA[i-j...i-j+k-1],也就是第一个和第三个棕色区间相等,接着又可以得到SA[j-k+1...j]=SA[i-k(i-1-k+1)...i-1],也就是第二个和第四个的棕色区间相等,然后!!!我们得到四个棕色区间相等!!!我们就可以判断SA[k+1]和SA[i]是否相等来求p[i],如果相等,p[i]=k+1,否则继续找p[k]。一直找啊找,直到找到没得找也就是当前的p[p[p[p[p[p[.......的值为0时,那么我们就判断SA[1]和SA[i]是否相同,相同p[i]=1,否则p[i]=0

  其实也不算暴力,而是利用p数组的定义来寻找前面可以用来继承的点!!

  但是到了这里,还没有完成KMP的学习

  我们的p数组只对SA字符串,也就是题目中的子串进行了处理

  那么p数组对于我们求答案有什么帮助呢?

  

  我们设j为SB字符串中的i-1位置开始所组成的后缀,与SA字符串中的1位置开始的前缀的最长公共长度,也就是SA[1...j]=SB[i-j(i-1-j+1)...j-1],那么我们可以判断SA[j+1]和SB[i]是否相等,相等就记录j的值,不相等就找p[j]!!!是不是似曾相识的步骤,没错!就是把SB中的i转移到SA中进行求解,这样就是KMP的全部了!


参考代码:

#include<cstdio>
#include<cstring>
using namespace std;
char sa[],sb[];//sa是母串,sb是子串
int p[];//p数组是为子串准备的,p的含义和母串一点关系都没有。
//p[i]表示sb中 以第i个字符为结尾,往前最多拉多少个字符(sb[i]结尾的后缀)可以完全匹配sb的前缀
//比如 sb="abcdabc"中 p[7]=3, 就这样,我当你理解p[i]的含义了
int main()
{
int lena,lenb,i,j;
scanf("%s",sa+);lena=strlen(sa+);
scanf("%s",sb+);lenb=strlen(sb+);
//制造P数组
p[]=;
for(i=;i<=lenb;i++)
{
j=p[i-];//先记录p[i-1]
while(j>&&sb[i]!=sb[j+]) j=p[j];
/*
这里有两种情况
如果j+1的位置的字符等于i位置的字符的话,那就直接把p[i]=j+1
否则就找j的p值,因为1~sb[p[j]]一定是1~sb[j]的后缀
*/
if(sb[i]==sb[j+]) p[i]=j+;else p[i]=;
}
int st,ed;
j=;
for(i=;i<=lena;i++)//紧接着用p数组来匹配母串
{
while(j>&&sa[i]!=sb[j+]) j=p[j];
if(sa[i]==sb[j+]) j++;
if(j==lenb){ed=i;st=i-lenb+;break;}//得到答案后就记录开头与结尾
}
if(j==lenb) printf("%d %d\n",st,ed);
else printf("NO\n");
return ;
}

算法导论————KMP的更多相关文章

  1. 经典算法系列--kmp

    前言 之前对kmp算法虽然了解它的原理,即求出P0···Pi的最大相同前后缀长度k:但是问题在于如何求出这个最大前后缀长度呢?我觉得网上很多帖子都说的不是很清楚,总感觉没有把那层纸戳破,后来翻看算法导 ...

  2. 【转】【经典算法】——KMP,深入讲解next数组的求解

    前言 之前对kmp算法虽然了解它的原理,即求出P0···Pi的最大相同前后缀长度k:但是问题在于如何求出这个最大前后缀长度呢?我觉得网上很多帖子都说的不是很清楚,总感觉没有把那层纸戳破,后来翻看算法导 ...

  3. "《算法导论》之‘字符串’":字符串匹配

    本文主要叙述用于字符串匹配的KMP算法. 阮一峰的博文“字符串匹配的KMP算法"将该算法讲述得非常形象,可参考之. 字符串‘部分匹配值’计算 KMP算法重要的一步在于部分匹配值的计算.模仿& ...

  4. B树——算法导论(25)

    B树 1. 简介 在之前我们学习了红黑树,今天再学习一种树--B树.它与红黑树有许多类似的地方,比如都是平衡搜索树,但它们在功能和结构上却有较大的差别. 从功能上看,B树是为磁盘或其他存储设备设计的, ...

  5. 红黑树——算法导论(15)

    1. 什么是红黑树 (1) 简介     上一篇我们介绍了基本动态集合操作时间复杂度均为O(h)的二叉搜索树.但遗憾的是,只有当二叉搜索树高度较低时,这些集合操作才会较快:即当树的高度较高(甚至一种极 ...

  6. 基本数据结构(2)——算法导论(12)

    1. 引言     这一篇博文主要介绍链表(linked list),指针和对象的实现,以及有根树的表示. 2. 链表(linked list) (1) 链表介绍      我们在上一篇中提过,栈与队 ...

  7. 堆排序与优先队列——算法导论(7)

    1. 预备知识 (1) 基本概念     如图,(二叉)堆是一个数组,它可以被看成一个近似的完全二叉树.树中的每一个结点对应数组中的一个元素.除了最底层外,该树是完全充满的,而且从左向右填充.堆的数组 ...

  8. quickSort算法导论版实现

    本文主要实践一下算法导论上的快排算法,活动活动. 伪代码图来源于 http://www.cnblogs.com/dongkuo/p/4827281.html // imp the quicksort ...

  9. 算法:KMP算法

    算法:KMP排序 算法分析 KMP算法是一种快速的模式匹配算法.KMP是三位大师:D.E.Knuth.J.H.Morris和V.R.Pratt同时发现的,所以取首字母组成KMP. 少部分图片来自孤~影 ...

随机推荐

  1. Spring MVC 搭建过程中web.xml配置引入文件的路径问题

    为啥要说一下这么low的问题,因为我是一个比较low的人,哈哈.本来我技术有限,没事干自己撘个环境找找乐趣,结果被各种基础问题,弄的一脸蒙蔽.算了不多说,直接说问题. 1.首先说一下java编译后的文 ...

  2. BZOJ1567 [JSOI2008]Blue Mary的战役地图(二分+二维hash)

    题意 问边长为n的两个正方形中最大的相等子正方形.(n<=50) 题解 用到了二维hash,感觉和一维的不太一样. 对于列行有两个不同的进制数然后也是通过类似前缀和的方法差分出一个矩形的hash ...

  3. 成长日记--记录在WB的第一个项目。

    具体为什么跑去外包,只能说自己太水了,或者太懒了,都不好好投简历,也没入这个坑过,如果有想去外包的,除非钱给到市场价的1.5倍以上,否则别考虑了. 项目是国内第一做通信公司的,从具体的需求说起比较好, ...

  4. 紫书 习题8-9 UVa 1613 (dfs染色+图的性质)

    这道题一开始我没想什么直接开始染, 但是是for循环一个节点一个节点染, 然后就WA 后了看了https://www.cnblogs.com/jerryRey/p/4702323.html 发现原来还 ...

  5. Linux gcc中的LIBRARY_PATH 和 LD_LIBRARY_PATH

    1. GNU 上关于LIBRARY_PATH的说明: LIBRARY_PATH The value of LIBRARY_PATH is a colon-separated list of direc ...

  6. Linux入门基础(一)

    UNIX/Linux 本身是没有图形界面的,我们通常在 UNIX/Linux 发行版上看到的图形界面实际都只是运行在 Linux 系统之上的一套软件XFree86,现在则是 xorg(X.Org),而 ...

  7. 6、python中的字符串

    最早的编码为ascii码,共256个符号.UTF-8是国际通用编码,全面支持中文,以一个字节表示英文,以三个字节表示一个中文以及其他语言:GB2312是我国自己定制的中文编码标准,使用1个字节表示英文 ...

  8. spfile

    1 让ORACLE自己主动从spfile启动  SQL> create spfile='/dev/vx/rdsk/vgora/lv_spfile' from pfile;  SQL> sh ...

  9. AVEVA PDMS to DIALux

    AVEVA PDMS to DIALux eryar@163.com   Abstract. DIAL develops DIALux - the world's leading software f ...

  10. 《鸟哥的Linux私房菜-基础学习篇(第三版)》(三)

    第2章 Linxu怎样学习         1. Linux当前的应用角色 当前的Linux常见的应用可略分为企业应用和个人应用双方面. 首先谈了企业环境的利用. 1)网络server. 2)关键任务 ...