经典算法KMP讲解,包含C++解法ACM模式

news2025/1/21 15:43:46

写在前面:一个人能走的多远不在于他在顺境时能走的多快,而在于他在逆境时多久能找到曾经的自己。——KMP

  • 讲解
    • 前置知识
    • 模拟next的构建
    • 匹配思路
      • 匹配字符串
      • 构建next数组
    • 模板代码
  • 题目一:KMP字符串
  • 题目二:找出字符串中第一个匹配项的下标

讲解

前置知识

首先,什么是KMP算法呢,就是字符串匹配算法,比如字符串a=abcd,字符串b=eeeeabcdeee,问a在b中出现的第一个下标,这就是KMP啦,字符串是顺序必须完全一样。如果只要求组合或者排列,比如ab和ba一样,这种情况一般用滑动窗口,俺们也写了滑动窗口讲解,保证学完嘎嘎乱杀滑动窗口->传送门

有些概念提前说一下:

  1. s[ ]是模式串,即比较长的字符串。
  2. p[ ]是匹配串,即比较短的字符串。(名字不重要,只要知道是找s里面的p就行
  3. 前缀:指除了最后一个字符以外,一个字符串的全部头部组合。
  4. 后缀:指除了第一个字符以外,一个字符串的全部尾部组合。
  5. “部分匹配值”:前缀和后缀的最长共有元素的长度。
  6. next数组,它存储的是每一个下标对应的“部分匹配值”,是KMP算法的核心

核心:
在每次失配时,不是把p串往后移一位,而是把p串往后移动至下一次可以和前面部分匹配的位置,这样就可以跳过大多数的失配步骤。而每次p串移动的步数就是通过查找next[ ]数组确定的。

首先看看暴力模式下的KMP
核心思路是不匹配时,同时回退s和p的指针,嵌套for,时间复杂度是O(MN)

// 暴力匹配(伪码)
// s是较长的串,p是较短的子串
int search(String s, String p) {
	int N = s.length;
    int M = p.length;
    for (int i = 0; i <= N - M; i++) {// i 是 s 串匹配的起点
        int j;
        for (j = 0; j < M; j++) {// j 是 p 子串匹配的起点
            if (pat[j] != txt[i+j])
                break;
        }
        // p 全都匹配了
        if (j == M) return i;
    }
    // s 中不存在 p 子串
    return -1;
}

假设:
s:a a a c a a a b
p:a a a b
s用指针 i 遍历,p用指针 j 遍历
当匹配到s[3]和p[3]的时候,两者不相等,而且p串中根本没有c,所以实际上根本没有必要回退 i 指针到 s[1] 的位置。

模拟next的构建

KMP的核心在于构建next数字,next数组记录了,当s和p不匹配时,p应该如何移动。
从头到尾s是不动的,且next数组的构造只和p有关
先说一下next数组的含义:next[j],是p[1, j ]串中前缀和后缀相同的最大长度,即 p[1, next[ j ] ] = p[ j - next[ j ] + 1, j ]。
在这里插入图片描述
解释:next[5]表示p[1,5]也就是abaab的最大前缀abaa和最大后缀也就是baab相同的最大长度,这里长度是ab,所以next[5] = 2

手动模拟求next数组:
p = abcab

p的字符abcab
下标12345
next[]00001

对next[ 4 ] :abca
前缀 = { a , ab , abc }
后缀 = { a . ca , bca }
next[ 4 ] = 1;
————————————————
对next[ 5 ] :abcab
前缀 = { a , ab , abc , abca }
后缀 = { b , ab , cab , bcab}
next[ 5 ] = 2;

匹配思路

匹配字符串

KMP主要分2步,求next数组和匹配字符串

先将匹配字符串:
s串和p串都是从1开始的,i从1开始,j从0开始
每次s[i]和p[j + 1]比较

如图:
当s到i,p到j+1的时候,发现不匹配了,就去next数组找应该退的位置,字符串中 ① == ② == ③,其中②和③相等是匹配的时候知道的,①和③是在求next数组知道的,所以直接将①移动到③的位置,这个操作由j = next[j]完成
在这里插入图片描述
代码:

for(int i = 1, j = 0; i <= n; i++)
{
    while(j && s[i] != p[j+1]) j = ne[j];
    //如果j有对应p串的元素, 且s[i] != p[j+1], 则失配, 移动p串
    //用while是由于移动后可能仍然失配,所以要继续移动直到匹配或整个p串移到后面(j = 0)

    if(s[i] == p[j+1]) j++;
    //当前元素匹配,j移向p串下一位
    if(j == m)
    {
        //匹配成功,进行相关操作
        j = next[j];  //继续匹配下一个子串,后面可能还有p子串
    }
}

构建next数组

这个地方真的很难理解,真的是抓耳饶腮

next数组是由p数组自己跟自己匹配完成的
代码和匹配的几乎一模一样
因为一个是s和p匹配,一个是p和p匹配
关键在于每次移动 i 前,将 i 前面已经匹配的长度记录到next数组中
在这里插入图片描述
代码:

for(int i = 2, j = 0; i <= m; i++)
{
    while(j && p[i] != p[j+1]) j = next[j];

    if(p[i] == p[j+1]) j++;

    next[i] = j;
}

模板代码

不是谁的AC代码

#include <iostream>
using namespace std;
const int N = 100010, M = 10010; //N为模式串长度,M匹配串长度

int n, m;
int ne[M]; //next[]数组,避免和头文件next冲突
char s[N], p[M];  //s为模式串, p为匹配串

int main()
{
    cin >> n >> s+1 >> m >> p+1;  //s+1和p+1会使下标从1开始
    //求next[]数组
    for(int i = 2, j = 0; i <= m; i++)
    {
        while(j && p[i] != p[j+1]) j = ne[j];
        if(p[i] == p[j+1]) j++;
        ne[i] = j;
    }
    //匹配操作
    for(int i = 1, j = 0; i <= n; i++)
    {
        while(j && s[i] != p[j+1]) j = ne[j];
        if(s[i] == p[j+1]) j++;
        if(j == m)  //满足匹配条件,打印开头下标, 从0开始
        {
            //匹配完成后的具体操作
            //如:输出以0开始的匹配子串的首字母下标
            //printf("%d ", i - m); (若从下标从0开始,加1)
            j = ne[j];            //再次继续匹配
        }
    }

    return 0;
}

题目一:KMP字符串

给定一个字符串 S,以及一个模式串 P,所有字符串中只包含大小写英文字母以及阿拉伯数字。模式串 P 在字符串 S 中多次作为子串出现。求出模式串 P 在字符串 S 中所有出现的位置的起始下标。
第一行输入n,表示p的长度
第二行输入p
第三行输入m,表示s的长度
第四行输入s
输出所有出现位置的起始下标

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10,M = 1e6 + 10;
int n, m;
int ne[N];
char s[M], p[N];
int main(){
	//p+1和s+1使数组起始下标为1,只有数组可以这样用
    cin >> n >> p + 1 >> m >> s + 1;
    //建ne数组
    for(int i = 2, j = 0; i <= n; i ++){
        while(j && p[i] != p[j + 1]) j = ne[j];
        if(p[i] == p[j + 1]) j ++;
        ne[i] = j;//将当前匹配的前缀后缀长度记录到next数组中
    }
    //匹配
    for(int i = 1, j = 0; i <= m; i ++){
        while(j && s[i] != p[j + 1]) j = ne[j];
        if(s[i] == p[j + 1]) j ++;
        if(j == n){
            cout << i - n << " ";
            j = ne[j];
        }
    }
    return 0;
}

题目二:找出字符串中第一个匹配项的下标

在这里插入图片描述
注意,ne数组应该初始化为-1
另外,由于这里字符串都是下标0开始的,所以i,j起始相比较模板代码-1

class Solution {
public:
    int strStr(string haystack, string needle) {
        int n = haystack.size();
        int m = needle.size();
        vector<int> ne(m, -1);
        // 建next数组
        for(int i = 1, j = -1; i < m; i ++){
            while(j != -1 && needle[i] != needle[j + 1]) j = ne[j];
            if(needle[i] == needle[j + 1]) j ++;
            ne[i] = j;
        }
        // 匹配
        for(int i = 0, j = -1; i < n; i ++){
            while(j != -1 && haystack[i] != needle[j + 1]) j = ne[j];
            if(haystack[i] == needle[j + 1]) j ++;
            if(j == m - 1){
                return i - m + 1;
            }
        }
        return -1;

    }
};

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

电脑开机启动项管理小工具,绿色免安装

HiBit Startup Manager 是一款功能强大的启动项管理工具&#xff0c;旨在帮助用户管理和优化计算机的自动启动程序。该软件通过添加或删除应用程序、编辑它们的属性以及管理流程、服务、任务调度程序和上下文菜单来实现这一目标。 HiBit Startup Manager 提供了以下主要功能&a…

Day82 代码随想录打卡|贪心算法篇---K次取反后最大化的数组和

题目&#xff08;leecode T1005&#xff09;&#xff1a; 给你一个整数数组 nums 和一个整数 k &#xff0c;按以下方法修改该数组&#xff1a; 选择某个下标 i 并将 nums[i] 替换为 -nums[i] 。 重复这个过程恰好 k 次。可以多次选择同一个下标 i 。 以这种方式修改数组后…

【C++】第一讲:入门概论

个人主页&#xff1a; 深情秋刀鱼-CSDN博客 C专栏&#xff1a;C程序设计 一、C发展历史 C的起源可以追溯到1979年&#xff0c;当时Bjarne Stroustrup(本贾尼斯特劳斯特卢普&#xff0c;这个翻译的名字不 同的地⽅可能有差异)在⻉尔实验室从事计算机科学和软件⼯程的研究⼯作。…

Linux中NFS配置

文章目录 一、NFS介绍1.1、NFS的工作流程1.2、NFS主要涉及的软件包1.3、NFS的主要配置文件 二、安装NFS2.1、更新yum2.2、安装NFS服务2.3、配置NFS服务器2.4、启动NFS服务2.5、配置防火墙&#xff08;如果启用了防火墙&#xff0c;需要允许NFS相关的端口通过&#xff09;2.6、生…

最新版Sonible Plugins Bundle v2024 winmac,简单智能,持续更新长期有效

一。Sonible Plugins Bundle v2024 win&mac Sonible Plugins Bundle是一款以创作者为中心的智能音频插件系列。这些工具的特点是易于使用&#xff0c;搭配高级处理和优质音质。pure:bundle的所有插件都由sonible的智能插件系列中使用的技术驱动&#xff0c;但在设计时考虑到…

论文解读(13)-StreetCLIP

原文&#xff1a; LEARNING GENERALIZED ZERO-SHOT LEARNERS FOR OPEN-DOMAIN IMAGE GEOLOCALIZATION StreetCLIP Preprint (arxiv.org) 摘要 本文的任务是Image geolocalization&#xff08;图像地理定位&#xff09; predicting the geographic coordinated of origin for …

【Material-UI】异步请求与Autocomplete的高效集成指南

文章目录 一、异步请求的两种用法1. 延迟加载&#xff08;Load on open&#xff09;实现方法 2. 动态搜索&#xff08;Search as you type&#xff09;实现方法 二、性能优化与注意事项1. 请求节流与去抖2. 禁用内置过滤3. 错误处理 三、实际应用案例&#xff1a;Google Maps P…

[数据集][目标检测]肾结石检测数据集VOC+YOLO格式1299张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;1299 标注数量(xml文件个数)&#xff1a;1299 标注数量(txt文件个数)&#xff1a;1299 标注…

目标跟踪那些事

目标跟踪那些事 跟踪与检测的区别 目标跟踪和目标检测是计算机视觉中的两个重要概念&#xff0c;但它们的目的和方法是不同的。 目标检测(object Detection)&#xff1a;是指在图像或视频帧中识别并定位一个或多个感兴趣的目标对象的过程 。 目标跟踪(object Tracking)&…

Java面试八股之Spring框架中使用到了哪些设计模式

Spring框架中使用到了哪些设计模式 Spring 框架是一个广泛使用的 Java 应用程序框架&#xff0c;它包含了许多设计模式的实现。以下是一些 Spring 框架中使用的设计模式&#xff1a; 工厂模式 (Factory Pattern) 描述&#xff1a;Spring 使用 BeanFactory 和 ApplicationCon…

深度优先遍历图--DFS

一. 前言 图的遍历定义&#xff1a;从已经给出的连通图中某一顶点出发&#xff0c;沿着一些边访遍图中所有的顶点&#xff0c;使每个顶点仅被访问一次&#xff0c;就叫做图的遍历&#xff0c;它是图的基本运算。 图的遍历实质&#xff1a;找每个顶点的邻接点的过程。 在找顶点…

【C语言】Top K问题【建小堆】

前言 TopK问题&#xff1a;从n个数中&#xff0c;找出最大&#xff08;或最小&#xff09;的前k个数。 在我们生活中&#xff0c;经常会遇到TopK问题 比如外卖的必吃榜&#xff1b;成单的前K名&#xff1b;各种数据的最值筛选 问题分析 显然想开出40G的空间是不现实的&#…

【目标检测实验系列】YOLOv5高效涨点:基于NAMAttention规范化注意力模块,调整权重因子关注有效特征(文内附源码)

1. 文章主要内容 本篇博客主要涉及规范化注意力机制&#xff0c;融合到YOLOv5(v6.1版本&#xff0c;去掉了Focus模块)模型中&#xff0c;通过惩罚机制&#xff0c;调整特征权重因子&#xff0c;使模型更加关注有效特征&#xff0c;助力模型涨点。 2. 简要概括 论文地址&#x…

2024-08-04 C# 中 string 实用技巧级新手常见错误

文章目录 1 方法重载1.1 string.Split()1.2 string.Indexof() 2 方法对比2.1 Contains2.2 Equals2.3 字符串差值 3 StringBuilder4 换行符4.1 推荐做法4.2 换行符混合问题 5 文件路径分隔5.1 推荐做法 6 测试代码6.1 "OnlySplit()" vs "SplitWithTrim()"6.…

三十种未授权访问漏洞复现 合集( 二 )

未授权访问漏洞介绍 未授权访问可以理解为需要安全配置或权限认证的地址、授权页面存在缺陷&#xff0c;导致其他用户可以直接访问&#xff0c;从而引发重要权限可被操作、数据库、网站目录等敏感信息泄露。---->目录遍历 目前主要存在未授权访问漏洞的有:NFS服务&a…

杂粮饼:健康与美味的完美融合

在美食的世界里&#xff0c;杂粮饼以其独特的魅力吸引着众多食客。这种看似平凡的美食&#xff0c;却蕴含着丰富的营养和令人陶醉的口感。杂粮饼&#xff0c; 顾名思义&#xff0c;是由多种杂粮混合制作而成。常见的杂粮如玉米、小米、高粱、燕麦等&#xff0c;它们各自带着独特…

Ability框架介绍

Ability Ability是应用所具备能力的抽象&#xff0c;也是应用程序的基本组成部分&#xff0c;主要包括组件生命周期回调、系统环境变化通知、应用跳转、卡片开发等能力。 Ability框架模型两种形态 FA模型Stage模型 Stage模型 Stage模型中的应用组件是由Ability这个基础概念…

LLM动手实践(一): 微调google的bert和vit模型完成文本和图片的分类任务

1. 写在前面 最近大模型比较火热&#xff0c;也正好在公司开始接触这块相关的业务&#xff0c;大模型是未来的趋势&#xff0c;对于研发工程师来讲&#xff0c;是powerful的效能工具&#xff0c;所以想沉淀一些大模型实践相关的笔记来记录自己在使用大模型产品&#xff0c;部署…

基于的X86+FPGA轨道交通模块化计算机,标准3U无风扇,支持国产化定制

支持Intel Socket G2 Mobile Sandy/Ivy Bridge i7/i5/i3处理器,Intel QM67/QM77,2*LAN,2*USB2.0,2*USB3.0,3*COM,3U ◆ 支持Intel Socket G2 Mobile Sandy/Ivy Bridge i7/i5/i3处理器,Intel QM67/QM77 ◆ 1*DDR3 SO-DIMM内存 ◆ 1*VGA,1*HDMI ◆ 2*LAN,2*USB2.0,2*USB3.0,3*…

Opencv学习-图像连接(vconcat函数和hconcat函数)

1. vconcat函数介绍&#xff08;竖向连接&#xff09; void cv::vconcat(const Mat * src, size_t nsrc, OutputArray dst ) src&#xff1a;Mat矩阵类型的数组。 nsrc&#xff1a;数组中 Mat 类型数据的个数。 dst&#xff1a;连接后的 Mat类矩阵。 该函数对存放在数组矩阵中…