数据结构——串——KMP算法

1.KMP算法是什么?

KMP算法是一个模式匹配算法,可以大大避免重复遍历的情况(也就是避免掉了传统的朴素模式匹配算法的低效)

因此我们KMP算法用于解决的就是字符串匹配问题

因此,假设我们有两个串,一个文本串,一个模式串

文本串:AABAABAAF

模式串:AABAAF

我们要进行匹配,传统的朴素算法是将底下模式串的第一位与文本串的第一位匹配,配对上了都走下一位,不匹配了就让模式串的第一位与文本串的第二位进行匹配,然后反复进行以上操作,直到匹配了 整个模式串才停止,但是这样真的高效吗?

我们在第一轮,只有最后一个没有匹配上,未必让两个串的指针都回溯,只需要让模式串指针回溯即可,比如说最后一个F出错了,我们可以选择回溯到模式串的第四个(A),在对上述字符串进行匹配,如果匹配到了,就继续,没有匹配到就继续回溯我们的模式串指针。

2.那么我们该如何确定要回溯的位置呢?

我们通常会用一个next数组来确定返回哪个位置,我们的next数组其实就是一个前缀表(同时也可以称为最长相等前后缀

(1)前缀表的基础

前缀:必须包含第一个字母不包含最后一个字母连续子串
后缀:必须包含最后一个字母不包含第一个字母连续子串
前缀表:最长相等前后缀(即这个字符串的前缀和后缀相等,还是最长的那个)

那么我们就来举一个例子,例如模版串是AABAAF

前一个:A,只有一个字母,没有前缀也没有后缀,最长相等前后缀为0;

前两个:AA,前缀为A,后缀为A,最长相等前后缀为1;

前三个:AAB,前缀为A,AA,后缀为B,AB,最长相等前后缀为0;

前四个:AABA,前缀为A,AA,AAB,后缀为A,BA,ABA,最长相等前后缀为1;

前五个:AABAA,前缀为A,AA,AAB,AABA,后缀为,A,AA,BAA,ABAA,最长相等前后缀为2;

前六个:AABAAF,前缀为A,AA,AAB,AABA,AABAA,后缀为F,AF,AAF,BAAF,ABAAF,最长相等前                 后缀为0;

我们回退之后就可以不重复比较了,这是为什么呢?

因为前一位的next数组值记录了该子串的最长相等前后缀,那么我们只需要从最长相等的前缀下一位开始进行比较就可以,而由于数组的下标从0开始,个数从1开始,所以next数组所记录的个数刚好就是最长前缀下一位的下标 

(2)next数组的代码求法

void getnext1()//得到next数组 
{int i=-1,j=0;//i是前缀末尾位置,j是后缀末尾位置 next1[0]=-1;//标记next数组首位置是-1 ,我们这里用的求next数组是前缀表向右错一个位置的写法while(j<len2)//后缀末尾没有超过模版串的最后 {if(i==-1||s2[i]==s2[j])//当现在是开头或者匹配上的时候 {i++; j++;next1[j]=i;}else{i=next1[i];//回溯前缀末尾}}
}

3.KMP算法的实现过程

KMP算法,就是当你本位置回溯失败之后,要回溯到你当前位置next数组所指向的位置,这样可以避免朴素暴力解法的重复匹配,减小时间开销

代码实现:

void kmp()
{int i=0,j=0;//i是文本串指针,j是模式串指针 while(i<len1)//当没有超过文本串时 {if(j==-1||s1[i]==s2[j])//当文本串指针指向初始位置,或者说文本串和模式串匹配上的时候{i++;//文本串指针向后挪一位j++;//模式串指针向后挪一位}else{j=next1[j];//如果匹配不上,就返回当前位置的next数组}if(j==len2){printf("YES");//如果能匹配完,说明文本串里有模式串,可以匹配上,输出YES}}
}

4.KMP算法相应例题

 (1)第一题:模版KMP

 

题解:这题就是一个标准的kmp的题,没有任何的难度,得到next数组后直接进行kmp进行匹配

如果能够达到模式串的末尾,就说明能够进行匹配,然后输出匹配的起始位置即可,即i-len(2)+1

然后后续输出next数组;

#include <bits/stdc++.h>
using namespace std;
int n,k,len1,len2;
int next1[1000001];
char s1[1000001];//文本串 
char s2[1000001];//模版串 
void getnext1()//得到next数组 
{int i=-1,j=0;//j是后缀末尾位置,i是前缀末尾位置 next1[0]=-1;//标记next数组首位置是-1 while(j<len2)//后缀末尾没有超过模版串的最后 {if(i==-1||s2[i]==s2[j])//当现在是开头或者匹配上的时候 {i++; j++;next1[j]=i;}else{i=next1[i];}}
}void kmp()
{int i=0,j=0;//i是文本串指针,j是模式串指针 while(i<len1)//当没有超过文本串时 {if(j==-1||s1[i]==s2[j]){i++;j++;}else{j=next1[j];}if(j==len2){printf("%d\n",i-len2+1);j=next1[j];}}
}
int main()
{ scanf("%s",s1);scanf("%s",s2);len1=strlen(s1);len2=strlen(s2);getnext1();kmp();for(int i=1;i<=len2;++i) printf("%d ",next1[i]);return 0;
}

 (2)第二题:无线传输

 

题解:这题其实更加方便我们去了解kmp的真正过程,以及了解next数组的真正含义,通过题目我们就会发现,其实s2的最短长度,其实就是L-next[L];

因此AC代码:

#include<bits/stdc++.h>
using namespace std;
int l;
char s[1000005];
int len;
int sum;
int next1[1000005];
void getnext1()
{int i=-1,j=0;next1[0]=-1;while(j<len){if(i==-1||s[i]==s[j]){i++;j++;next1[j]=i;} else{i=next1[i];}}} int main(){scanf("%d",&l);scanf("%s",s);len=strlen(s);getnext1();printf("%d",l-next1[l]);return 0;}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/484477.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

市场复盘总结 20240222

仅用于记录当天的市场情况&#xff0c;用于统计交易策略的适用情况&#xff0c;以便程序回测 短线核心&#xff1a;不参与任何级别的调整&#xff0c;采用龙空龙模式 一支股票 10%的时候可以操作&#xff0c; 90%的时间适合空仓等待 二进三&#xff1a; 进级率中 25% 最常用…

如何使用CanaryTokenScanner识别Microsoft Office文档中的Canary令牌和可疑URL

关于CanaryTokenScanner CanaryTokenScanner是一款功能强大的Canary令牌和可疑URL检测工具&#xff0c;该工具基于纯Python开发&#xff0c;可以帮助广大研究人员快速检测Microsoft Office和Zip压缩文件中的Canary令牌和可疑URL。 在网络安全领域中&#xff0c;保持警惕和主动…

【STM32学习】——续上:软件SPI读写W25Q64SPI通信外设硬件SPI读写W25Q64

四、软件SPI读写W25Q64 工程思路与I2C类似&#xff0c;MySPI.c是通信底层&#xff0c;主要包括通信引脚封装、初始化、SPI通信的三个拼图&#xff08;起始、终止和交换一个字节&#xff09;&#xff1b;基于此文件建立W25Q64.c&#xff0c;调用MySPI三个拼图&#xff0c;拼接成…

Git合并固定分支的某一部分至当前分支

在 Git 中&#xff0c;通常使用 git merge 命令来将一个分支的更改合并到另一个分支。如果你只想合并某个分支的一部分代码&#xff0c;可以使用以下两种方法&#xff1a; 1.批量文件合并 1.1.创建并切换到一个新的临时分支 首先&#xff0c;从要合并的源分支&#xff08;即要…

Promise中的链式流

如果阅读有疑问的话&#xff0c;欢迎评论或私信&#xff01;&#xff01; 本人会很热心的阐述自己的想法&#xff01;谢谢&#xff01;&#xff01;&#xff01; 携手共进&#xff01; 文章目录 前言深入Promise链式流 前言 在探索Promise链式流之前我们要知道两个Promise固有…

快速将excel/word表格转换为web页面(html)的方法

前言 在进行开发企业信息化建设的过程&#xff0c;应该有很多这样的场景&#xff0c;就是将现有的电子表格记录的方式转换为在数据系统中进行网页上报。也就是需要根据当前一直使用的表格制作一个上传这个表格信息的网页&#xff0c;如果要减少系统的使用学习成本&#xff0c;…

Vue3之ref与reactive的基本使用

ref可以创建基本类型、对象类型的响应式数据 reactive只可以创建对象类型的响应式数据 接下来让我为大家介绍一下吧&#xff01; 在Vue3中&#xff0c;我们想让数据变成响应式数据&#xff0c;我们需要借助到ref与reactive 先为大家介绍一下ref如何使用还有什么注意点 我们需…

考研高数(高阶导数的计算)

1.归纳法 常见高阶导数 2.泰勒展开式 3.莱布尼兹公式 4.用导数定义证明导函数在某一点连续的例题

MLflow【部署 01】MLflow官网Quick Start实操(一篇学会部署使用MLflow)

一篇学会部署使用MLflow 1.版本及环境2.官方步骤Step-1 Get MLflowStep-2 Start a Tracking ServerStep 3 - Train a model and prepare metadata for loggingStep 4 - Log the model and its metadata to MLflowStep 5 - Load the model as a Python Function (pyfunc) and us…

alibabacloud学习笔记06(小滴课堂)

讲Sentinel流量控制详细操作 基于并发线程进行限流配置实操 在浏览器打开快速刷新会报错 基于并发线程进行限流配置实操 讲解 微服务高可用利器Sentinel熔断降级规则 讲解服务调用常见的熔断状态和恢复 讲解服务调用熔断例子 我们写一个带异常的接口&#xff1a;

【海贼王的数据航海:利用数据结构成为数据海洋的霸主】时间复杂度 | 空间复杂度

目录 1 -> 算法效率 1.1 -> 如何衡量一个算法的好坏&#xff1f; 1.2 -> 算法的复杂度 2 -> 时间复杂度 2.1 -> 时间复杂度的概念 2.2 -> 大O的渐进表示法 2.3 -> 常见时间复杂度计算 3 -> 空间复杂度 4 -> 常见复杂度对比 1 -> 算法效…

IO进程线程

IO练习 互斥机制练习 #include <myhead.h> int num 520; pthread_mutex_t mutex;void *task1(void *arg) {printf("11111111\n");pthread_mutex_lock(&mutex);num 1314;sleep(3);printf("task1:num%d\n",num);pthread_mutex_unlock(&mut…