KMP是一个困扰我很久的算法,听老师或者是学姐讲了差不多有4次了,但是还是搞不太懂,今天终于,终于,终于搞懂了!

                          ——2017-10-29 Vanora

首先推荐一下KMP详解——July

读罢之后内心只有一个感觉:我的KMP终于可以毕业了qwq

学东西千万不要求快!细细地,慢慢地去读这篇文章,相信你也可以从头到尾彻底理解KMP算法呦~

接下来是一些KMP的练手题:

做完这些并且真正搞懂之后,相信你一定就会KMP算法了~(一定要理解了,吃透了!)

1.P3375 【模板】KMP字符串匹配

直通车

思路:

  真正意义上的KMP板子题,很良心(不加优化版)

新知识Get!!!:

  char数组不清零,直接用cin读入的话,自动清零!!!好厉害的样子!!!

坑点:

  一定要真正理解nxt[]求得到底是什么,是用匹配串求解还是用文本串

上代码:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std; const int M = ;
char s[M],p[M];
int lens,lenp; int nxt[M];
void getnxt() {
int k=-,j=;
nxt[]=-;
while(j<lenp) {
if(k==- || p[j]==p[k]) {
j++;k++;
nxt[j]=k; //记录更新的nxt值
}
else k=nxt[k];
}
} void KMP() {
int i=,j=;
while(i<lens) {
if(j==- || s[i]==p[j]) i++,j++;
//j==-1:没有nxt,所以从一开始进行匹配
//s[i]==p[j]:当前文本串与匹配串的位置上的字符匹配成功,继续匹配
else j=nxt[j]; //寻找更短的公共前后缀
if(j==lenp) {
printf("%d\n",i-lenp+);
i--; //文本串之前已经匹配过了,所以没有再次进行匹配的需要了
j=; //匹配串从新开始进行匹配
}
}
} int main() {
cin>>s>>p;
lens=strlen(s);
lenp=strlen(p);
getnxt();
KMP();
for(int i=; i<=lenp; i++) printf("%d ",nxt[i]);
return ;
}

2.POJ  3461  Oulipo

直通车

思路:

  用KMP来做,如果成功匹配,那么ans++,最后输出ans即可

坑点:

  ①注意他的输入顺序。。。。。。
  ②注意是多组数据输入。。。。。
  ③该题用上述优化过的方法都过不了。。。。
  ④该题数据范围一定要看好了。。。
  ⑤W,T范围是不同的。。
  ⑥最好不要用gets读入。

上代码:

1)while版(mine)

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std; const int M = ;
const int N = ;
char s[M],p[N];
int T,ans,lens,lenp; int nxt[N];
void getnxt() {
int k=,j=;
nxt[]=-;nxt[]=;
while(j<lenp) {
if(k==- || p[j]==p[k]) {
k++,j++;
nxt[j]=k;
}
else k=nxt[k];
}
} void KMP() {
int i=,j=;
while(i<lens) {
if(s[i]==p[j]) i++,j++;
else if(j>=) j=nxt[j];
else i++,j=;
if(j==lenp) ans++,j=nxt[j];
}
} int main() {
scanf("%d",&T);
while(T--) {
scanf("%s%s",p,s);
lens=strlen(s);
lenp=strlen(p);
getnxt();
KMP();
printf("%d\n",ans);
ans=;
}
return ;
}

2)从网上学到的另外一种方法,感觉十分类似,然而能AC。。。服气。。。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std; const int M = ;
const int N = ;
char s[M],p[N];
int ans,lens,lenp; int nxt[N];
void getnxt() {
int k=,j;
nxt[]=-;nxt[]=;
for(j=; j<=lenp; j++) {
while(k>= && p[k]!=p[j-]) k=nxt[k];
nxt[j]=++k;
}
} void KMP() {
int i=,j=;
while(i<lens) {
if(s[i]==p[j]) i++,j++;
else if(j>=) j=nxt[j];
else i++,j=;
if(j==lenp) ans++,j=nxt[j];
}
} int main() {
int T;
scanf("%d",&T);
while(T--) {
scanf("%s%s",p,s);
lens=strlen(s);
lenp=strlen(p);
getnxt();
KMP();
printf("%d\n",ans);
ans=;
}
return ;
}

3)采用for循环的一种方法

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std; const int M = ;
const int N = ;
int T,ans,lens,lenp;
char s[M],p[N]; int nxt[N];
void getnxt() {
nxt[]=;
for(int i=; i<lenp; i++) {
int j=nxt[i];
while(j && p[i]!=p[j]) j=nxt[j];
nxt[i+] = p[i]==p[j] ? j+ : ;
}
} void KMP() {
int j=;
for(int i=; i<lens; i++) {
while(j && (p[j]!=s[i])) j=nxt[j];
if(p[j]==s[i]) j++;
if(j==lenp) ans++;
}
} int main() {
scanf("%d",&T);
while(T--) {
memset(nxt,,sizeof(nxt));
ans=;
scanf("%s%s",p,s);
lenp=strlen(p);
lens=strlen(s);
getnxt();
KMP();
printf("%d\n",ans);
}
return ;
}

3.P1308 统计单词数

直通车

思路:

  ①因为会出现空格,所以手动将s,p的左右均+' ',然后再进行KMP即可

  ②不需要+' ',直接在KMP匹配成功之后判断是否s左右均拥有' '即可

      不过需要注意的是:当在一开头就匹配成功的时候则只需要判断是否s右边有' '即可

坑点:

  千万不要用gets进行输入,会编译错误。。。。

上代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
using namespace std; const int pl = ;
int lens,lenp,ans=-,firsta=-;
string s,p; int nxt[pl];
void getnxt() {
int k=,j=;
nxt[]=-,nxt[]=;
while(j<lenp) {
if(k==- || p[k]==p[j]) {
k++,j++;
nxt[j]=k;
}
else k=nxt[k];
}
} void KMP() {
int i=,j=;
while(i<lens) {
if(s[i]==p[j]) i++,j++;
else if(j>=) j=nxt[j];
else i++,j=;
if(j==lenp) {
if(firsta==-) firsta=i-j;
ans++,j=nxt[j];
}
}
} void tochange(string &a,int len) {
for(int i=; i<len; i++) {
if(a[i]==' ') continue;
int x=a[i]-'A';
if(x>=) x-=;
char y=x+'a';
a[i]=y;
}
} int main() {
getline(cin,p);
p=' '+p+' ';
lenp=p.length();
tochange(p,lenp);
getline(cin,s);
s=' '+s+' ';
lens=s.length();
tochange(s,lens);
getnxt();
KMP();
if(ans==-) printf("%d",ans);
else printf("%d %d",ans+,firsta);
return ;
}

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
using namespace std; const int pl = ;
int lens,lenp,ans=-,firsta=-;
string s,p; int nxt[pl];
void getnxt() {
int k=,j=;
nxt[]=-,nxt[]=;
while(j<lenp) {
if(k==- || p[k]==p[j]) {
k++,j++;
nxt[j]=k;
}
else k=nxt[k];
}
} void KMP() {
int i=,j=;
while(i<lens) {
if(s[i]==p[j]) i++,j++;
else if(j>=) j=nxt[j];
else i++,j=;
if(j==lenp && s[i]==' ') {
if(i>j && s[i-j-]==' ') {
if(firsta==-) firsta=i-j;
ans++,j=nxt[j];
} else {
if(i==j) {
// if(i==0) { //等价
if(firsta==-) firsta=i-j;
ans++,j=nxt[j];
}
}
}
}
} void tochange(string &a,int len) {
for(int i=; i<len; i++) {
if(a[i]==' ') continue;
int x=a[i]-'A';
if(x>=) x-=;
char y=x+'a';
a[i]=y;
}
} int main() {
getline(cin,p);
lenp=p.length();
tochange(p,lenp);
getline(cin,s);
lens=s.length();
tochange(s,lens);
getnxt();
KMP();
if(ans==-) printf("%d",ans);
else printf("%d %d",ans+,firsta);
return ;
}

【説明する】KMP的更多相关文章

  1. POJOの説明

    参考URL: https://baike.baidu.com/item/POJO/3311958?fr=aladdin https://wenku.baidu.com/view/eba89bbcf12 ...

  2. 【説明する】hash

    首先对于判重,我们能想到的方法有什么呢? 1)bool数组 2)set(集) 数组与集合的优缺点: 1.因为集合是对数组做的封装,所以,数组永远比任何一个集合要快. 2.数组声明了它容纳的元素的类型, ...

  3. 【説明する】DS

    其实就是数据结构课后题整理....只会一个是什么鬼 染色问题: 线段树? 功能太强大了! 我们并不需要那么多的功能 运用并查集!!! 将相同的并为一段 BZOJ 2375(讲真我没找到这个题在哪里.. ...

  4. 【説明する】STL

    作为C++标准不可缺少的一部分,STL应该是渗透在C++程序的角角落落里的. STL不是实验室里的宠儿,也不是程序员桌上的摆设,她的激动人心并非昙花一现. 所以今天要整理的东西就是STL!(orz 杨 ...

  5. UbuntuでPostgreSQLをインストールからリモートアクセスまでの手順

    PostgreSQLサーバの立ち上げに少しハマりましたので.メモしておきます. OS: Ubuntu14.04 LTS インストール 最初はPostgreSQLをインストールします.普通にapt-ge ...

  6. VirtualBox 共享文件夾

    説明:host為window10,guest為centos7 一.安装VBoxLinuxAdditions 1. 在guest上挂载virtualbox安装目录下的VBoxGuestAdditions ...

  7. Ruby中字符串与正则表达式的问题

    Ruby的正则表达式为Regexp类的对象 主要的元语言字符 記号 意味 例 説明 ^ 行頭 /^abc/ abcで始まる行 $ 行末 /abc$/ abcで終わる行 . 任意の1文字 /a.b/ a ...

  8. WebMidiLink

    g200kg > WebMidiLink > 1.Introduction WebMidiLink 2012/06/26 1.Introduction « Prev 1.Introduct ...

  9. xpath 参考

    using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Te ...

随机推荐

  1. PHP正则 正向预查&反向预查

    了解正向预查&反向预查前,我们先要知道正则的2个函数:preg_match_all . preg_replace preg_match_all 可以看文章:点击查看 preg_replace ...

  2. git 去除版本控制

    git会进入当前文件目录, 然后执行如下命令: find . -name ".git" | xargs rm -Rf 该项目就会去除git的版本控制了.再修改的话也不会影响git的 ...

  3. sping+redis实现消息队列的乱码问题

    使用spring支持redis实现消息队列,参考官方样例:https://spring.io/guides/gs/messaging-redis/ 实现后在运行过程中发现消费者在接收消息时会出现乱码的 ...

  4. iOS圆弧渐变进度条的实现

    由于项目需要一个环形渐变进度条显示课程,这方便网上的确有很多相关资料但是,都是比较零散的而且,大多数只是放一堆代码就算完了.这里我想详细写一篇我自己实现这个进度条的过程. 实现一个圆弧进度条主要分为三 ...

  5. SAP Kyma(Extension Factory on SAP Cloud Platform)的架构简介

    SAP kyma主要分三大块组成: (1) Application connector simplify and securely connect external systems to Kyma a ...

  6. 二零一八阿里p7笔试116题

    1. junit用法,before,beforeClass,after, afterClass的执行顺序 2. 分布式锁 3. nginx的请求转发算法,如何配置根据权重转发 4. 用hashmap实 ...

  7. Docker pull下载出现 error pulling image configuration:

    出现这个问题,并且在错误信息的最后附带 net/http: TLS handshake timeout: 猜测是docker的相关配置问题,导致无法通过TLS握手 执行如下命令修改配置 echo &q ...

  8. 【翻译】--19C Oracle 安装指导

    18C新功能     1.简化的基于镜像的Oracle数据库安装     从18C开始,Oracle可以作为镜像文件来下载和安装,必须解压缩镜像文件到ORACLE_HOME目录,然后执行runInst ...

  9. 【2018-01-26】SqlServer 检查死锁和阻塞

    利用sys.sysprocesses SQL进程检查是否出现死锁和阻塞 Sys.SysProcesses 系统表是一个很重要的系统视图,主要用来定位与解决Sql Server的阻塞和死锁 select ...

  10. Django hrf:权限、频率控制

    一.权限 二.频率控制 一.权限 1.权限介绍 只有超级用户才能访问指定的数据,所以就要用权限组件进行设置 2.局部使用 # 单独抽出写一个视图类 from rest_framework.permis ...