很久之前学的了。

我很懒,不太喜欢画图。

做个笔记回忆一下:

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笔记的更多相关文章

  1. 字符串匹配KMP算法详解

    1. 引言 以前看过很多次KMP算法,一直觉得很有用,但都没有搞明白,一方面是网上很少有比较详细的通俗易懂的讲解,另一方面也怪自己没有沉下心来研究.最近在leetcode上又遇见字符串匹配的题目,以此 ...

  2. 字符串匹配-KMP

    节选自 https://www.cnblogs.com/zhangtianq/p/5839909.html 字符串匹配 KMP O(m+n) O原来的暴力算法 当不匹配的时候 尽管之前文本串和模式串已 ...

  3. zstu.4194: 字符串匹配(kmp入门题&& 心得)

    4194: 字符串匹配 Time Limit: 1 Sec  Memory Limit: 128 MB Submit: 206  Solved: 78 Description 给你两个字符串A,B,请 ...

  4. 字符串匹配KMP算法

    1. 字符串匹配的KMP算法 2. KMP算法详解 3. 从头到尾彻底理解KMP

  5. 字符串匹配--kmp算法原理整理

    kmp算法原理:求出P0···Pi的最大相同前后缀长度k: 字符串匹配是计算机的基本任务之一.举例,字符串"BBC ABCDAB ABCDABCDABDE",里面是否包含另一个字符 ...

  6. 字符串匹配KMP算法的C语言实现

    字符串匹配是计算机的基本任务之一. 举例来说,有一个字符串"BBC ABCDAB ABCDABCDABDE",我想知道,里面是否包含另一个字符串"ABCDABD" ...

  7. 字符串匹配KMP算法的讲解C++

    转自http://blog.csdn.net/starstar1992/article/details/54913261 也可以参考http://blog.csdn.net/liu940204/art ...

  8. 字符串匹配KMP算法(转自阮一峰)

    转自 http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html 字符串匹配是计算 ...

  9. 【Foreign】字符串匹配 [KMP]

    字符串匹配 Time Limit: 10 Sec  Memory Limit: 256 MB Description Input Output Sample Input 3 3 6 3 1 2 1 2 ...

  10. 【Luogu P3375】字符串匹配KMP算法模板

    Luogu P3375 模式串:即题目中的S2所代表的意义 文本串:即题目中的S1所代表的意义 对于字符串匹配,有一种很显然的朴素算法:在S1中枚举起点一位一位匹配,失配之后起点往后移动一位,从头开始 ...

随机推荐

  1. 【Nginx】Nginx访问静态资源

    Nginx访问静态资源 即通过IP:端口/文件名 访问文件实现. 修改Nginx配置 location / { # root html; # index index.html index.htm; a ...

  2. 【ssh】SSH连接远程主机的两种方式

    一.基于用户名与密码连接 指令 ssh username@server_ip 随后要求输入密码 加密流程 1️⃣ 在SSH连接建立过程中,客户端和服务器使用Diffie-Hellman密钥交换协议协商 ...

  3. 前端Vuer,请收好这份《Vue组件单元测试》宝典,给自己多一些安全感

    大家好,我是 Kagol. 作为一名前端,在做业务开发的过程中,你是否曾经: 因为担心上线之后出bug,而反复手工验证自己负责的模块 不敢修改现有的"屎山"(别人写的或者是自己1年 ...

  4. Microsoft edge锁定在任务栏上,被修改主页360的解决方法

    今天从桌面下边的任务栏打开Microsoft edge浏览器,突然发现主页被篡改为360导航了(生气!恶龙咆哮ooo 在桌面上是Microsoft edge,固定到任务栏就成为Microsoft ed ...

  5. mysql基础_约束

    介绍 约束对应的英语单词:constraint,在创建表的时候,我们可以给表中的字段加上一些约束,来保证这个表中数据的完整性.有效性. 约束的作用就是为了保证:表中的数据有效. 类型 非空约束:not ...

  6. 2021-7-29 MySql进阶

    Alter的使用: 列的增加和删减 alter table users add user_name VARCHAR(100);#添加一列在末尾 SELECT * from users; alter t ...

  7. 如何使用iptables防火墙模拟远程服务超时

    前言 超时,应该是程序员很不爱处理的一种状态.当我们调用某服务.某个中间件.db时,希望对方能快速回复,正确就正常,错误就错误,而不是一直不回复.目前在后端领域来说,如java领域,调用服务时以同步阻 ...

  8. Spring Boot通过企业邮箱发邮件被Gmail退回的问题解决方法

    这两天给我们开发的Chrome插件:Youtube中文配音增加了账户注册和登录功能,其中有一步是邮箱验证,所以这边会在Spring Boot后台给用户的邮箱发个验证信息.如果发邮件,之前的文章教程里就 ...

  9. 按关键字API接口搜索天眼查企业数据

    一.如果你想要查找某一个企业的基本信息或是对行业中的企业进行筛选,那么使用API接口搜索天眼查企业数据会非常方便. 首先,你需要获取天眼查API的access_token,这可以通过注册账号获取.一旦 ...

  10. 解决Nginx SSL 代理 Tomcat 获取 Scheme 总是 Http 问题

    背景 公司之前用的是http,但是出于苹果app审核和服务器安全性问题,要改为https,我们公司用的是沃通的ssl,按照沃通的官方文档提供的步骤完成服务器的配置. 架构上使用了 Nginx +tom ...