看了出题知识点才发现自己连KMP都没有好好的理解,甚至一共就打过一次板子=-=

于是照着之前的课件学了一学...发现没怎么弄懂qwq

我太弱啦!

找了一篇自认为全网最好的介绍 觉得写得很棒

字符串匹配的KMP算法

作者: 阮一峰

日期: 2013年5月 1日

字符串匹配是计算机的基本任务之一。

举例来说,有一个字符串"BBC ABCDAB ABCDABCDABDE",我想知道,里面是否包含另一个字符串"ABCDABD"?

许多算法可以完成这个任务,Knuth-Morris-Pratt算法(简称KMP)是最常用的之一。它以三个发明者命名,起头的那个K就是著名科学家Donald Knuth。

这种算法不太容易理解,网上有很多解释,但读起来都很费劲。直到读到Jake Boxer的文章,我才真正理解这种算法。下面,我用自己的语言,试图写一篇比较好懂的KMP算法解释。

1.

首先,字符串"BBC ABCDAB ABCDABCDABDE"的第一个字符与搜索词"ABCDABD"的第一个字符,进行比较。因为B与A不匹配,所以搜索词后移一位。

2.

因为B与A不匹配,搜索词再往后移。

3.

就这样,直到字符串有一个字符,与搜索词的第一个字符相同为止。

4.

接着比较字符串和搜索词的下一个字符,还是相同。

5.

直到字符串有一个字符,与搜索词对应的字符不相同为止。

6.

这时,最自然的反应是,将搜索词整个后移一位,再从头逐个比较。这样做虽然可行,但是效率很差,因为你要把"搜索位置"移到已经比较过的位置,重比一遍。

7.

一个基本事实是,当空格与D不匹配时,你其实知道前面六个字符是"ABCDAB"。KMP算法的想法是,设法利用这个已知信息,不要把"搜索位置"移回已经比较过的位置,继续把它向后移,这样就提高了效率。

8.

怎么做到这一点呢?可以针对搜索词,算出一张《部分匹配表》(Partial Match Table)。这张表是如何产生的,后面再介绍,这里只要会用就可以了。

9.

已知空格与D不匹配时,前面六个字符"ABCDAB"是匹配的。查表可知,最后一个匹配字符B对应的"部分匹配值"为2,因此按照下面的公式算出向后移动的位数:

  移动位数 = 已匹配的字符数 - 对应的部分匹配值

因为 6 - 2 等于4,所以将搜索词向后移动4位。

10.

因为空格与C不匹配,搜索词还要继续往后移。这时,已匹配的字符数为2("AB"),对应的"部分匹配值"为0。所以,移动位数 = 2 - 0,结果为 2,于是将搜索词向后移2位。

11.

因为空格与A不匹配,继续后移一位。

12.

逐位比较,直到发现C与D不匹配。于是,移动位数 = 6 - 2,继续将搜索词向后移动4位。

13.

逐位比较,直到搜索词的最后一位,发现完全匹配,于是搜索完成。如果还要继续搜索(即找出全部匹配),移动位数 = 7 - 0,再将搜索词向后移动7位,这里就不再重复了。

14.

下面介绍《部分匹配表》是如何产生的。

首先,要了解两个概念:"前缀"和"后缀"。 "前缀"指除了最后一个字符以外,一个字符串的全部头部组合;"后缀"指除了第一个字符以外,一个字符串的全部尾部组合。

15.

"部分匹配值"就是"前缀"和"后缀"的最长的共有元素的长度。以"ABCDABD"为例,

  - "A"的前缀和后缀都为空集,共有元素的长度为0;

  - "AB"的前缀为[A],后缀为[B],共有元素的长度为0;

  - "ABC"的前缀为[A, AB],后缀为[BC, C],共有元素的长度0;

  - "ABCD"的前缀为[A, AB, ABC],后缀为[BCD, CD, D],共有元素的长度为0;

  - "ABCDA"的前缀为[A, AB, ABC, ABCD],后缀为[BCDA, CDA, DA, A],共有元素为"A",长度为1;

  - "ABCDAB"的前缀为[A, AB, ABC, ABCD, ABCDA],后缀为[BCDAB, CDAB, DAB, AB, B],共有元素为"AB",长度为2;

  - "ABCDABD"的前缀为[A, AB, ABC, ABCD, ABCDA, ABCDAB],后缀为[BCDABD, CDABD, DABD, ABD, BD, D],共有元素的长度为0。

16.

"部分匹配"的实质是,有时候,字符串头部和尾部会有重复。比如,"ABCDAB"之中有两个"AB",那么它的"部分匹配值"就是2("AB"的长度)。搜索词移动的时候,第一个"AB"向后移动4位(字符串长度-部分匹配值),就可以来到第二个"AB"的位置。

(完)

#pragma GCC optimize("O2")
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<bitset>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#include<set>
#include<map>
#include<limits.h>
#include<ctime>
#define N 100001
typedef long long ll;
const int inf=0x3fffffff;
const int maxn=2017;
using namespace std;
inline int read()
{
int f=1,x=0;char ch=getchar();
while(ch>'9'||ch<'0')
{
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch<='9'&&ch>='0')
{
x=(x<<3)+(x<<1)+ch-'0';
ch=getchar();
}
return f*x;
}
void getnext(string a,int nxt[])
{
int len=a.length();
nxt[0]=0;
for(int i=1,j=0;i<len;i++)
{
while(j&&a[i]!=a[j])j=nxt[j-1];
if(a[i]==a[j])j++;
nxt[i]=j;
}
}
int kmp(string a,string b,int nxt[])
{
int ans=0;
int len1=a.length();
int len2=b.length();
for(int i=0,j=0;i<len1;i++)
{
while(j&&b[j]!=a[i])
j=nxt[j-1];
if(b[j]==a[i])
j++;
if(j==len2)
ans++;
}
return ans;
}
string a,b;
int nxt[N];
int main()
{
cin>>a>>b;
cout<<kmp(a,b,nxt);
}

这里KMP算法所求出来的是B串在A串中的出现次数,同理我们也可以求出B串在A串的出现位置,因为每次j==len2的时候实质上都是找到了一次匹配 那么我们对于每一次出现这样的匹配 去保存一下当前的i-len+2,计入ans数组即可

KMP模板实现的更多相关文章

  1. hdu 1686 KMP模板

    // hdu 1686 KMP模板 // 没啥好说的,KMP裸题,这里是MP模板 #include <cstdio> #include <iostream> #include ...

  2. Oulipo HDU 1686 KMP模板

    题目大意:求模式串在主串中的出现次数. 题目思路:KMP模板题 #include<iostream> #include<algorithm> #include<cstri ...

  3. KMP模板(bin)

    KMP模板 主要是kuangbin的模板,之后加了一点我的习惯和理解. kmpN() 作用:构造next数组 参数:模式串,模式串长度 kmpC() 作用:返回模式串在主串中出现的次数(可重复) 参数 ...

  4. HDU 1711 - Number Sequence - [KMP模板题]

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1711 Time Limit: 10000/5000 MS (Java/Others) Memory L ...

  5. HDU 1711 Number Sequence(KMP模板)

    http://acm.hdu.edu.cn/showproblem.php?pid=1711 这道题就是一个KMP模板. #include<iostream> #include<cs ...

  6. 剪花布条---hdu2087(kmp模板)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2087 kmp模板题: #include <cstdio> #include <cst ...

  7. Oulipo----poj3461(kmp模板)

    题目链接:http://poj.org/problem?id=3461 和 减花布条 的题对比一下: 求s2中s1的个数kmp模板: #include<stdio.h> #include& ...

  8. kmp模板 && 扩展kmp模板

    kmp模板: #include <bits/stdc++.h> #define PB push_back #define MP make_pair using namespace std; ...

  9. kuangbin专题16B(kmp模板)

    题目链接: https://vjudge.net/contest/70325#problem/B 题意: 输出模式串在主串中出现的次数 思路: kmp模板 在 kmp 函数中匹配成功计数加一, 再令 ...

  10. [HDU1711]KMP模板

    解题关键:1.直接套kmp模板即可,注意最后输出的位置,需要在索引的位置+1. 2.next用作数组名在oj中会编译错误, 3.选用g++,只有g++才会接受bits/stdc++.h OJ中g++和 ...

随机推荐

  1. 2018-2019 20165237网络对抗 Exp4 恶意代码分析

    2018-2019 20165237网络对抗 Exp4 恶意代码分析 实验目标 1.1是监控你自己系统的运行状态,看有没有可疑的程序在运行. 1.2是分析一个恶意软件,就分析Exp2或Exp3中生成后 ...

  2. # 20175333曹雅坤《Java程序设计》第五周学习总结

    教材学习内容总结 第六章要点: 1.接口:1)接口声明: interface //接口的名字 2)接口体 2.实现接口:类实现接口:一个类需要在类声明中使用关键字implements声明该类实现一个或 ...

  3. input子系统 KeyPad-Touch上报数据格式与机制【转】

    转自:https://www.cnblogs.com/0822vaj/p/4185634.html -------------------------------------------------- ...

  4. java程序员技术范围

    1 工具 开发工具.源代码管理.构建工具.测试工具(压力.安全等).接口测试工具.反编译工具.日志工具.第三方工具等 2 java jvm.多线程.socket.io(两种方式).集合(两大接口).异 ...

  5. javascript 常用方法 解析URL,补充前导字符

    2018-11-7 20:41:20 星期三 1. 解析URL function parseUrl(url){ url = decodeURIComponent(url); var u = url.s ...

  6. 【JS】Javascript数组操作

    1.提取数组中每个对象的某个属性组成新的数组 如数组为: let arry = [ { name: 'zhao', 'age': 18 }, { name: 'qian', 'age': 19 }, ...

  7. ORACLE根据两个表都含有的字段条件来判断两个表连接后有没有数据

    A表  字段1 字段2 B表  字段1 字段3 A表的字段1=B表的字段1 SELECT DISTINCT A.字段2 FROM TABEL1  A ,TABEL2  B  WHERE  A.字段1 ...

  8. 非root用户加入docker用户组省去sudo

    服务器环境:centos7.6.1810,Docker version 18.09.3 1.使用有sudo权限的帐号登录到服务器系统,如:test用户 2.新建用户组docker之前,查看用户组中有没 ...

  9. J2EE_Maven_POM文件配置的详解(转)

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/20 ...

  10. linux 大杂烩

    1.linux mysql输错命令 mysql命令行中执行多行命令时,如果前边输入的命令发生错误,是很令人头疼的事情,可以通过输入\c来取消前边的输入,重新进入“mysql->”提示符. 2.l ...