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. 解决GitHub下载慢问题,不用修改HOSTS文件

    写这篇文章缘由是我用的一款Github上的软件软件版本更新,想去Github上下载新的版本,结果下载速度居然只有几k,还老是下载失败,然后去修改HOSTS文件(我看文章基本都是叫修改这个),修改完成后 ...

  2. svn之合并分支

    学习连接 svn的merge使用例子

  3. Flutter笔记(一)

    Android/iOS移动端开发 原生开发 Android原生应用通常指使用Java或Kotlin语言直接调用Android SDK开发的应用程序:而iOS原生应用通常指使用Objective-C或S ...

  4. css 关于"浮动边距加倍"及其解决方法-------解决方案是在这个div里面加上display:inline;

    写div代码的时候,经常发现明明宽度算得很准确,但是莫明其妙的会和计划的布局不太一样- -|||开始以为自己代码写的有问题,拼命找bug也一无所获,最后可能会稍微修改样式来达到想要的效果,但终究也是外 ...

  5. pymsql及事务

    MySQL知识点补充 1.去重 distinct select distinct name,age from t1; # 针对查找出来的结果整行(记录)进行去重,也就是相同行只保存一个 注意点:dis ...

  6. dubbo框架梳理

    Dubbo分层 Dubbo框架运行主要分如下九层: 配置层:config 服务代理层:proxy 注册中心层:registry 路由层:cluster 监控层:monitor 远程调用层:protoc ...

  7. [LeetCode] 283. Move Zeroes ☆(移动0到最后)

    描述 给定一个数组nums,写一个函数,将数组中所有的0挪到数组的末尾,维持其他所有非0元素的相对位置. 举例: nums = [0, 1, 0, 3, 12], 函数运行后结果为[1, 3, 12, ...

  8. tornado基本使用一

    一.tornado web程序编写思路 . 创建web应用实例对象,第一个初始化参数为路由映射列表 . 定义实现路由映射列表中的handler类 . 创建服务器实例, 绑定服务器端口 . 启动当前线程 ...

  9. JavaScript基础概念与语法

    学习了一些最基础的JavaScript语法: <!DOCTYPE html> <html lang="en"> <head> <meta ...

  10. Image Processing and Analysis_21_Scale Space:Edge Detection and Ridge Detection with Automatic Scale Selection——1998

    此主要讨论图像处理与分析.虽然计算机视觉部分的有些内容比如特 征提取等也可以归结到图像分析中来,但鉴于它们与计算机视觉的紧密联系,以 及它们的出处,没有把它们纳入到图像处理与分析中来.同样,这里面也有 ...