字符串匹配|kmp笔记
很久之前学的了。
我很懒,不太喜欢画图。
做个笔记回忆一下:
kmp
朴素比对字符串
所谓字符串匹配,是这样一种问题:“字符串 T 是否为字符串 S 的子串?如果是,它出现在 S 的哪些位置?” 其中 S 称为主串;T 称为模式串。如在字符串s abcabcabcabd 中找到子串T abcabd :
先设两个指针i、j,i表示S的指针,j表示T的指针
i=j=0
↓(i)
abcabcabcabd
abcabd
↑(j)
匹配成功,移动指针(i++,j++)
↓
abcabcabcabd
abcabd
↑
匹配成功,移动指针(i++,j++)
.
.
.
↓
abcabcabcabd
abcabd
↑
c≠d,回溯(i=1,j=0)
↓
abcabcabcabd
abcabd
↑
b≠a,回溯(i=2,j=0)
.
.
.
↓
abcabcabcabd
abcabd
↑
匹配成功,移动指针(i++,j++)
↓
abcabcabcabd
abcabd
↑
匹配成功,移动指针(i++,j++)
↓
abcabcabcabd
abcabd
↑
.
.
.
↓
abcabcabcabd
abcabd
↑
匹配成功,找到模式串(print(i))
优化
上面的复杂度是 O(nm) ,为什么这么多,发现是回溯花费时间过多。我们合理的希望是i不回溯,即:
先设两个指针i、j,i表示S的指针,j表示T的指针
i=j=0
↓(i)
abcabcabcabd
abcabd
↑(j)
匹配成功,移动指针(i++,j++)
↓
abcabcabcabd
abcabd
↑
匹配成功,移动指针(i++,j++)
.
.
.
↓
abcabcabcabd
abcabd
↑
c≠d,i不回溯,因为ab已经匹配完了,所以我们跳到上一个ab的位置(j=2)
↓
abcabcabcabd
abcabd
↑
匹配成功,移动指针(i++,j++)
↓
abcabcabcabd
abcabd
↑
匹配成功,移动指针(i++,j++)
↓
abcabcabcabd
abcabd
↑
匹配成功,移动指针(i++,j++)
↓
abcabcabcabd
abcabd
↑
a≠d,i不回溯(j=2)
↓
abcabcabcabd
abcabd
↑
匹配成功,移动指针(i++,j++)
.
.
.
↓
abcabcabcabd
abcabd
↑
匹配成功,找到模式串(print(i))
全程i不会减少
nxt数组
我们假设知道一个叫做nxt的数组,代表下一个j,当匹配失败时就可以 j=nxt[j] 来防止i的回溯。那么我们可以快速算出他的子串,如下代码:
int KMP(){
for(int i=0,j=0;i<n;i++){
while(j>0 && str[i]!=pnt[j]){
j=nxt[j-1]; // 为什么是 nxt[j-1],因为第j位和第i位已经不匹配了,j-1位和i-1位才是匹配的,所以用j=nxt[j-1]
}
if(str[i]==pnt[j]){
j++; // 匹配成功
}
if(j==m){ // 匹配成功
return i-j+1;
}
}
return -1;
}
nxt数组是什么
nxt代表重复真子集长度,和回文串差不多,但不是回文串。区别
回文串:abccba
重复真子集:abcabc
欸,那么我们可以看出当已经有不匹配:
↓
abcabcabcabcd
abcabcd
↑
因为前面的abc已经匹配完了,我们不需要回溯回去再匹配,只需要跳到上一个abc的位置就行了。
↓
abcabcabcabcd
abcabcd
↑
我们nxt储存的就是与它重复的这部分的位置。以 abcababdabc 为例:
a:0(因为是真子集,不包括自身)
ab:0
abc:0
_ _
abca:1
__ __
abcab:2
_ _
abcaba:1
__ __
abcabab:2
abcababd:0
_ _
abcababda:1
__ __
abcababdab:2
___ ___
abcababdabc:3
那么我们会发现,他们重复这部分的下标(以0开始)刚好就是重复真子集长度:
有S=abcabcabd
T=abcabd
当匹配到:
↓
abcabcabd
abcabd
↑
时,说明前面的ab已经配好了,我们移动到上一个也有ab的地方:
↓
abcabcabd
abcabd
↑
即可成功匹配
计算nxt数组
我们可以用递推的思想,先设有nxt[0]=0(必然的),然后设有快指针i=1,慢指针j=0,刚好,我们会发现重复部分的长度也是j的值。
对于匹配成功,则j++
对于匹配失败,则从上一位nxt中找到重复部分回溯j。
看不懂就看一下计算过程吧
计算abcabdabcabc的nxt,ij定义同上,上面箭头表示i,下面箭头表示j
↓(i)
abcabdabcabc
↑(j)
不相同,故nxt[i(1)]=0
↓(i++,下不再阐述)
abcabdabcabc
↑
不相同,故nxt[i(2)]=0,j不变(因为j是0,不必回溯)
↓
abcabdabcabc
↑
相同,故j++,nxt[i(3)]=1
↓
abcabdabcabc
↑
相同,故j++,nxt[i(4)]=2
↓
abcabdabcabc
↑
不相同,故j回溯到nxt[j-1(1)]的重复长度(0)
↓
abcabdabcabc
↑
无法再回溯,nxt[i(5)]=0
↓
abcabdabcabc
↑
相同,故j++,nxt[i(6)]=1
↓
abcabdabcabc
↑
相同,故j++,nxt[i(7)]=2
↓
abcabdabcabc
↑
相同,故j++,nxt[i(8)]=3
↓
abcabdabcabc
↑
相同,故j++,nxt[i(9)]=4
↓
abcabdabcabc
↑
相同,故j++,nxt[i(10)]=5
↓
abcabdabcabc
↑
不相同,故j回溯到nxt[j-1(4)]的重复长度(2)
↓
abcabdabcabc
↑
发现相等,j++,nxt[i(11)]=j=3
遍历完成,退出
代码如下:
void makeNext(){
nxt[0]=0;
for(int i=1,j=0;i<m;i++){
while(j>0 && pnt[i]!=pnt[j]){
j=nxt[j-1]; // 因为nxt表示重复部分的下标,我们可以回溯回去
}
if(pnt[i]==pnt[j]){
j++;
}
nxt[i]=j;
}
}
代码:
#include<cstdio>
#include<cstring>
#include<string>
char str[1010],pnt[1010];
int n,m;
int nxt[1010];
void makeNext(){
nxt[0]=0;
for(int i=1,j=0;i<m;i++){
while(j>0 && pnt[i]!=pnt[j]){
j=nxt[j-1];
}
if(pnt[i]==pnt[j]){
j++;
}
nxt[i]=j;
}
}
int KMP(){
for(int i=0,j=0;i<n;i++){
while(j>0 && str[i]!=pnt[j]){
j=nxt[j-1];
}
if(str[i]==pnt[j]){
j++;
}
if(j==m){
return i-j+1;
}
}
return -1;
}
int main(){
scanf("%s %s",str,pnt);
n=strlen(str);
m=strlen(pnt);
makeNext();
printf("%d",KMP());
}
字符串匹配|kmp笔记的更多相关文章
- 字符串匹配KMP算法详解
1. 引言 以前看过很多次KMP算法,一直觉得很有用,但都没有搞明白,一方面是网上很少有比较详细的通俗易懂的讲解,另一方面也怪自己没有沉下心来研究.最近在leetcode上又遇见字符串匹配的题目,以此 ...
- 字符串匹配-KMP
节选自 https://www.cnblogs.com/zhangtianq/p/5839909.html 字符串匹配 KMP O(m+n) O原来的暴力算法 当不匹配的时候 尽管之前文本串和模式串已 ...
- zstu.4194: 字符串匹配(kmp入门题&& 心得)
4194: 字符串匹配 Time Limit: 1 Sec Memory Limit: 128 MB Submit: 206 Solved: 78 Description 给你两个字符串A,B,请 ...
- 字符串匹配KMP算法
1. 字符串匹配的KMP算法 2. KMP算法详解 3. 从头到尾彻底理解KMP
- 字符串匹配--kmp算法原理整理
kmp算法原理:求出P0···Pi的最大相同前后缀长度k: 字符串匹配是计算机的基本任务之一.举例,字符串"BBC ABCDAB ABCDABCDABDE",里面是否包含另一个字符 ...
- 字符串匹配KMP算法的C语言实现
字符串匹配是计算机的基本任务之一. 举例来说,有一个字符串"BBC ABCDAB ABCDABCDABDE",我想知道,里面是否包含另一个字符串"ABCDABD" ...
- 字符串匹配KMP算法的讲解C++
转自http://blog.csdn.net/starstar1992/article/details/54913261 也可以参考http://blog.csdn.net/liu940204/art ...
- 字符串匹配KMP算法(转自阮一峰)
转自 http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html 字符串匹配是计算 ...
- 【Foreign】字符串匹配 [KMP]
字符串匹配 Time Limit: 10 Sec Memory Limit: 256 MB Description Input Output Sample Input 3 3 6 3 1 2 1 2 ...
- 【Luogu P3375】字符串匹配KMP算法模板
Luogu P3375 模式串:即题目中的S2所代表的意义 文本串:即题目中的S1所代表的意义 对于字符串匹配,有一种很显然的朴素算法:在S1中枚举起点一位一位匹配,失配之后起点往后移动一位,从头开始 ...
随机推荐
- CentOS 8搭建Docker镜像私有仓库-registry
目录 简介 安装Docker 添加docker yum源 安装docker 确保网络模块开机自动加载 使桥接流量对iptables可见 配置docker 验证docker是否正常 添加用户到docke ...
- C++笔记(2)——函数
六. 函数 6.1 函数基础 一个典型的函数(function)定义包括:返回类型(return type).函数名字,由0或多个形参(parameter)组成的列表以及函数体.我们通过调用运算符来执 ...
- Redis理论
什么是Redis Redis(Remote Dictionary Server)是使用C语言编写的,开源的(BSD许可)高性能非关系型(NoSQL)的键值对数据库. Redis可以存储键和五种不同类型 ...
- QPushButton中常用的方法
常用方法如下所示: setCheckable():设置按钮是否已经被选中,如果设置为True,则表示按钮将保持已点击和释放状态. toggl():在按钮之间进行切换 setIcon():设置按钮上的图 ...
- 从MybatisPlus回归Mybatis
从MybatisPlus回归Mybatis 之前写项目一直习惯使用MyBatisPlus,单表查询很方便:两张表也很方便,直接业务层处理两张表的逻辑.但什么都图方便只会害了你. 但连接的表比较复杂的时 ...
- Midjourney的一些学习心得:如何高效的图生图
注意本文没有什么长篇大论,全部是自己的学习心得. 心得体会:如何图生图 今天在一篇midjourney看到好图应该怎么抄. 相信经常会看到好图也想要的,但是要么抄不出感觉,要么抄过来把水印也抄了,这一 ...
- 如何通过抖音订单API接口获取订单详情
要通过抖音订单API接口获取订单详情,您需要进行以下步骤: 1.获取Access Token:使用APP ID和APP Secret调用获取Access Token API接口来获取您的Access ...
- Hadoop环境安装与配置
1.基础操作系统环境安装(略) 2.JDK的安装与配置 当前各大数据软件如Hadoop等,仍然停留在Java 8上,在本实验选用的是Java 8.在自己的Linux系统中,jdk可以使用如下命令进行一 ...
- 支持JDK19虚拟线程的web框架,之三:观察运行中的虚拟线程
欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览 本篇是<支持JDK19虚拟线程的web ...
- Vue 中的 Ajax
1.1 使用代理服务器 1.1.1 方式一 在 vue.config.js 中添加如下配置: devServer:{ proxy:"http://localhost:5000" } ...