看了出题知识点才发现自己连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. EasyUI整合篇

    easy ui combobox getValue 获取不到值问题 必须设置属性showblank: true,否则只能从onSelect事件中获取 $("#ddlType").c ...

  2. Linux-Shell编程之数组操作

    源码 #!/bin/bash str="Array - Demo Shell"; echo ${#str} #求字符串長度 #定義 arr=('a' 'b' 'c' 'd' 'e' ...

  3. vue api学习之nextTick的理解

    对于 Vue.nextTick 方法,之前没有听说过,突然听到别人提起,貌似作用挺大.以下为学习心得.官方文档上这样定义:在下次 DOM 更新循环结束之后执行延迟回调.在修改数据之后立即使用这个方法, ...

  4. C#创建 WebApi 项目

    做web api 可以参考一下网友 C#进阶系列——WebApi 接口参数不再困惑:传参详解 - 懒得安分 - 博客园https://www.cnblogs.com/landeanfen/p/5337 ...

  5. [经验交流] CentOS7 docker 使用 overlay 存储

    关于 docker overlay 存储相比 device mapper 的优势,可以在 google 上搜索相关资料.在 ubuntu 16.04.2 上可以直接使用 overlay 存储,而在 C ...

  6. LVS节点健康检查及管理脚本

    在LVS负载均衡主节点上,模拟keepalived健康检查功能管理LVS节点,当节点挂掉从服务器池中剔除,好了再加到服务器池中来. 工具:yum install -y ipvsadm web03:10 ...

  7. java学习笔记--从c/c++到java转变

    final修饰符1)final变量final表示“最后的,最终的”含义,变量一旦赋值后,不能被重新赋值.被final修饰的实例变量必须显示指定初始值.final修饰符通常和static修饰符一起来创建 ...

  8. C++ opentracing zipkin

    Useful page : https://github.com/openzipkin/b3-propagation & other official websites Steps to ru ...

  9. iOS开发之获取当前展示的VC

    /** 递归查找当前显示的VC*/ + (UIViewController *)recursiveFindCurrentShowViewControllerFromViewController:(UI ...

  10. C# 知识点回忆..

    方便查阅: 数据结构与算法 1.线性表: (1)数据结构2 - 线性表 (2)数据结构和算法 c#– 1.单项链表 委托和事件 委托1:C#4.0图解教程 - 第15章 委托 委托2:<C#本质 ...