浅谈KMP算法——Chemist
很久以前就学过KMP,不过一直没有深入理解只是背代码,今天总结一下KMP算法来加深印象。
一、KMP算法介绍
- KMP解决的问题:给你两个字符串A和B(|A|=n,|B|=m,n>m),询问一个字符串在另一个字符串中的每一次出现位置。
- 暴力:枚举长串中的每一个起点,然后一位一位判断是否与短串完全相同,枚举复杂度是O(n),比较的复杂度是O(m),总的时间复杂度是O(nm),时间复杂度比较差
- 引入两个定义:
1.匹配串(A):被匹配的长串。
2.模式串(B):在匹配串中每次找出现位置的短串。
- 在匹配的过程中,我们相当于是要每次找到匹配串的一个前缀的后缀与模式串的一个前缀完全相同,也就是说,我们需要在匹配串上维护一个指针i,在模式串上维护一个指针j,使得Ai-j+1~i = B1~j 。
举个例子:匹配串为abbabab,模式串为abbaa,当A[i]==B[j]时直接往后继续找,当i=5,j=5时会产生失配,这时按照我们暴力的想法,我们会将i退回的到2位置,将j退回到1的位置,然后重复此过程,然而我们考虑一下就会发现这一段是一个字符也匹配不上的,因为A1~5已经和B1~5匹配上了,如果A2~6可以和B1~5匹配,则说明A1~5与A2~6完全相同,然而显然不同,于是我们有了新的思路,当我们失配时,我们缩小j,直到A的以i为结尾的前缀的后缀与B的长度为j的前缀完全相同,也就是说找到一个最大的k使得Ai-k+1~i=B1~j(k<j)。那么如何快速找到k呢?
- 这里我们引入next数组,next[j]表示模式串中长度为j-1的前缀中最长的前缀等于后缀的长度,那么当A[i]与B[j]失配时j需要往前移j-next[j]位,显然next[1]=0。
- 如何求出next数组?自己与自己匹配,如果可以匹配上,即Bnext[j-1]+1=Bj,那么next[j]=next[j-1]+1,如果不能匹配上,就让next往回跳直到可以匹配。(如果不理解可以自行画图模拟,很容易就可以理解。)
- KMP算法的大致思路就介绍完了,就是先让模式串自己与自己匹配求出next数组,然后用next数组辅助与匹配串匹配。时间复杂度为O(N+M)。
代码(洛谷P3375):
#include<bits/stdc++.h>
using namespace std;
const int MAXX=1e6+;
char s1[MAXX],s2[MAXX];
int l1,l2,next[MAXX];
//next[i]表示s2中以i为结尾的非前缀子串与A的前缀能够匹配的最大长度
void KMP()
{
for(int i=,j=;i<l2;i++){
while(j&&s2[i]!=s2[j])
j=next[j];//匹配不到就往下找
if(s2[i]==s2[j])j++;
next[i+]=j;
}
for(int i=,j=;i<l1;i++){
while(j&&s1[i]!=s2[j])j=next[j];
if(s1[i]==s2[j])j++;
if(j==l2){
//找到一次出现
printf("%d\n",i-l2+);
}
}
}
int main()
{
cin>>s1;cin>>s2;
l1=strlen(s1);l2=strlen(s2);
KMP();
for(int i=;i<=l2;i++)
printf("%d ",next[i]);
return ;
}
二、KMP小应用
- 1.(口胡题意)给你一个长度为n的字符串S,求它的最小循环节,n<=100000。
x是S的循环节的等价条件是S1~n-x+1=Sx+1~n,最小化x相当于最大化n-x+1,也就是求出next[n],然后n-next[n]几位答案。代码略。
- 2.CF126B Password(https://www.luogu.org/problemnew/show/CF126B)
一句话题意:给你一个字符串S(|S|<=1000000),找到既是S前缀又是S后缀又在S中间出现过(既不是S前缀又不是S后缀)的最长子串。
既是前缀又是后缀的最长子串很容易找,直接用上面的KMP求出next数组,next[n]即为答案,而中间出现过的子串就相当于一个前缀的后缀,所以我们只需找到不等于n的next[i]中最大的i,然后让next[n]不断往前跳直到他的长度小于前面的最大长度,这是找到这个位置往后的长度个字符构成的字符串就是答案,如果在寻找的过程中next[n]跳到0还没有找到说明无解。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
char a[];
int next[],n,maxx=;
void cal()//求出next数组
{
next[]=;
for(int i=,j=;i<=n;i++){
while(j&&a[i]!=a[j+])j=next[j];
if(a[i]==a[j+])j++;
next[i]=j;
if(i!=n)maxx=max(next[i],maxx);
//找到next数组的最大值
}
}
int main()
{
cin>>a+;
n=strlen(a+);
cal();
int x=next[n];
if(x==)printf("Just a legend\n");
else{
while(x>maxx)x=next[x];
//找到小于max{next[1~n-1]}的最大匹配长度
if(x==){
printf("Just a legend\n");
return ;
}
for(int i=;i<n;i++)
if(x==next[i]){
for(int j=i-next[i]+;j<=i;j++)
//i-next[i]+1为答案子串的左端点
printf("%c",a[j]);
printf("\n");
return ;
}
}
return ;
}
浅谈KMP算法——Chemist的更多相关文章
- 浅谈KMP算法及其next[]数组
KMP算法是众多优秀的模式串匹配算法中较早诞生的一个,也是相对最为人所知的一个. 算法实现简单,运行效率高,时间复杂度为O(n+m)(n和m分别为目标串和模式串的长度) 当字符串长度和字符集大小的比值 ...
- 单模式串匹配----浅谈kmp算法
模式串匹配,顾名思义,就是看一个串是否在另一个串中出现,出现了几次,在哪个位置出现: p.s. 模式串是前者,并且,我们称后一个 (也就是被匹配的串)为文本串: 在这篇博客的代码里,s1均为文本串, ...
- 浅谈KMP算法
一.介绍 烤馍片KMP算法是用来处理字符串匹配问题的.比如说给你两个字符串A,B,问B是不是A的子串? 比如,eg就是aeggx的子串 一般讲字符串A称为主串,用来匹配的B串称为模式串 定义n为字符串 ...
- 【字符串算法3】浅谈KMP算法
[字符串算法1] 字符串Hash(优雅的暴力) [字符串算法2]Manacher算法 [字符串算法3]KMP算法 这里将讲述 [字符串算法3]KMP算法 Part1 理解KMP的精髓和思想 其实KM ...
- 【文文殿下】浅谈KMP算法next数组与循环节的关系
KMP算法 KMP算法是一种字符串匹配算法,他可以在O(n+m)的时间内求出一个模式串在另一个模式串下出现的次数. KMP算法是利用next数组进行自匹配,然后来进行匹配的. Next数组 Next数 ...
- 浅谈 KMP 算法
最近在复习数据结构,学到了 KMP 算法这一章,似乎又迷糊了,记得第一次学习这个算法时,老师在课堂上讲得唾沫横飞,十分有激情,而我们在下面听得一脸懵比,啥?这是个啥算法?啥玩意?再去看看书,完全听不懂 ...
- 浅谈分词算法(5)基于字的分词方法(bi-LSTM)
目录 前言 目录 循环神经网络 基于LSTM的分词 Embedding 数据预处理 模型 如何添加用户词典 前言 很早便规划的浅谈分词算法,总共分为了五个部分,想聊聊自己在各种场景中使用到的分词方法做 ...
- 浅谈分词算法(4)基于字的分词方法(CRF)
目录 前言 目录 条件随机场(conditional random field CRF) 核心点 线性链条件随机场 简化形式 CRF分词 CRF VS HMM 代码实现 训练代码 实验结果 参考文献 ...
- 浅谈分词算法(3)基于字的分词方法(HMM)
目录 前言 目录 隐马尔可夫模型(Hidden Markov Model,HMM) HMM分词 两个假设 Viterbi算法 代码实现 实现效果 完整代码 参考文献 前言 在浅谈分词算法(1)分词中的 ...
随机推荐
- Java面试题总结之Java基础(三)
1.JAVA 语言如何进行异常处理,关键字:throws,throw,try,catch,finally分别代表什么意义?在try 块中可以抛出异常吗? 答:Java 通过面向对象的方法进行异常处理, ...
- 【转】海量数据处理算法-Bloom Filter
1. Bloom-Filter算法简介 Bloom Filter(BF)是一种空间效率很高的随机数据结构,它利用位数组很简洁地表示一个集合,并能判断一个元素是否属于这个集合.它是一个判断元素是否存在于 ...
- poj 1695 Magazine Delivery 记忆化搜索
dp[a][b][c],表示三个人从小到大依次在a,b.c位置时.距离结束最少的时间. 每次选一个人走到c+1位置搜索就好了. 坑点在于不能floyd.预计题目没说清楚.意思就是假设没送Li,那么Li ...
- Angular团队公布路线图,并演示怎样与React Native集成
本文来源于我在InfoQ中文站翻译的文章,原文地址是:http://www.infoq.com/cn/news/2015/06/angular-2-react-native-roadmap 前不久在旧 ...
- Office WORD WPS如何设置PPT播放全屏
1 在设计-页面设置中,幻灯片大小改成自定义,高度和宽度如下图所示.(我个人的笔记本是15.6存的宽屏笔记本,你可以根据自己笔记本的比例修改宽度和高度的数据来或者不同的比例值),注意在HDMI的输出方 ...
- soapUI系列之—-04 问题解决 获取接口返回报文response报错
1. SoapUI+Groovy中"org.apache.xmlbeans.XmlException: error: Unexpected element: CDATA" 通过So ...
- 解决echart在IE中使用时,在div中加入postion后图表不显示问题
<!-- 为ECharts准备一个具备大小(宽高)的Dom --> <div id="main" style="height:400px;width:1 ...
- cmake使用演示样例与整理总结
本文代码托管于github cmake_demo cmake中一些提前定义变量 PROJECT_SOURCE_DIR project的根文件夹 PROJECT_BINARY_DIR 执行cmake命 ...
- C/C++ scanf 函数中%s 和%c 的简单差别
首先声明:在键盘中敲入字符后,字符会首先保存在键盘缓冲区中供scanf函数读取(scanf.getchar等函数是读取缓冲区,getch函数是读取的控制台信息,即为直接从键盘读取).另外特别注意键盘上 ...
- 【solr专题之中的一个】Solr高速入门
一.Solr学习相关资料 1.官方材料 (1)高速入门:http://lucene.apache.org/solr/4_9_0/tutorial.html.以自带的example项目高速介绍发Solr ...