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. c++学习(二)------this指针学习

    在c++中,类的不同实例有自己的数据(储存在不同地方),有很多拷贝.而类的成员函数却只有一份备份. 而不同的类的实例却可以调用同一个函数,这是通过this指针来完成的. *this代表当前类本身,th ...

  2. shell习题第21题:计算数字的个数

    [题目要求] 计算文档a.txt中每一行出现数字的个数并且要计算一下整个文档中一共出现了几个数字 例如a.txt如下: sdhhyh776dbbgbfg dhhdffhhhs556644382 运行结 ...

  3. (六)lucene之其他查询方式(组合查询,制定数字范围、指定字符串开头)

    本章使用的是lucene5.3.0 指定数字范围查询 package com.shyroke.test; import java.io.IOException; import java.nio.fil ...

  4. 在论坛中出现的比较难的sql问题:6(动态行转列 考试科目、排名动态列问题)

    原文:在论坛中出现的比较难的sql问题:6(动态行转列 考试科目.排名动态列问题) 所以,觉得有必要记录下来,这样以后再次碰到这类问题,也能从中获取解答的思路. 下面的几个问题,都是动态行转列的问题. ...

  5. 【web】Chrome 浏览器中查看 webSocket 连接信息

    1.以下代码实现一个webSocket连接,在文本输入框中输入内容,点击发送,通过服务器,返回相同的内容显示在下方. 1 <!DOCTYPE html> 2 <html lang=& ...

  6. JS基础 sessionStorage

    html5中的Web Storage包括了两种存储方式:sessionStorage和localStorage. sessionStorage用于本地存储一个会话(session)中的数据,这些数据只 ...

  7. 1 vue 关键字解释

    1 每一个计算属性都包含一个getter和一个setter,计算属性可以依赖其他计算属性,计算属性可以依赖当前vue实例的数据也可以依赖其他vue实例的数据 2 计算属性是基于它的依赖缓存的,方法则是 ...

  8. el-select 可选择可输入

    <el-select v-model="saveWardForm.wardCode" placeholder="" filterable @blur=&q ...

  9. C++ STL 之 常用算法

    #include <iostream> #include <vector> #include <algorithm> using namespace std; // ...

  10. MySQL 查询大于“时间字段”15分钟、1小时、1天的数据

    以下代码中times为时间字段,类型为datetime 1.查询大于times十五分钟的数据 //大于号后面都是获取times十五分钟后的时间select*from table where now() ...