【算法】KMP字符串匹配算法

目录

一、暴力

二、KMP

2.1 思路

2.2 next数组

2.3 实现

2.4 例题


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

一、暴力

在进行字符串匹配时,暴力匹配是我们想到的最简单的方法,即将子串与主串当中的每一个子串进行匹配。如果当前子串不匹配,则回溯到主串中下一个子串的开头重新进行匹配

在最坏的情况下,暴力匹配的时间复杂度为O(N*M),N为主串长度,M为子串长度


二、KMP

2.1 思路

在上面的例子中我们可以注意到,在子串中,不匹配的字符'C'前面的子串似乎有一些规律

可以看到,这部分子串的前缀后缀是相同的。如果我们能够将前缀移动到后缀的位置,是不是就能避免重复的比较了呢?

解释一下字符串的前缀和后缀:前缀就是从字符串开头到任意位置的一段字符串,后缀就是从字符串任意位置到字符串结尾的一段字符串

可以看到,通过这种方式,上面的箭头不需要向前回溯,下面的箭头也不需要回到子串的开头了,时间复杂度变为线性。这就是KMP算法的核心思想

但是我们怎样才能知道子串中每个位置的最长相同前后缀长度呢?这里就需要一个数组来维护了

2.2 next数组

next数组用于存放以下标i为结尾的子串最长相同前后缀长度,例如:

以下标0为结尾的子串"A",只有自己一个字符,因此最长相同前后缀长度为0(此处前后缀不能为子串自身)

在代码实现中,我们可以用一个f和i,f代表最长相同前缀结尾位置,i代表最长相同后缀结尾位置

以下标1为结尾的子串"AB",子串[f]和子串[i]不相同,且f=0,因此最长相同前后缀长度为0,i向后移动

以下标2为结尾的子串"ABA",子串[f]和子串[i]相同,有最长相同前后缀"A",因此最长相同前后缀长度为1,f和i都向后移动

以下标3为结尾的子串"ABAB",子串[f]和子串[i]相同,加上上一步的结果,有最长相同前后缀"AB",因此最长相同前后缀长度为2,f和i都向后移动

以下标4为结尾的子串"ABABC",此时子串[f]和子串[i]不同,f回到next[f-1]下标处

此时f=0,但子串[f]和子串[i]还是不同,因此最长相同前后缀长度为0。i到达子串结尾,结束匹配

构建next数组的代码如下:

int ne[N];

void buildNext(string &p)
{
    int front = 0; //front为最长相同前缀结尾
    int i = 1; //i为最长相同后缀结尾
    while(i < n)
    {
        if(p[i] == p[front]) //前后缀结尾字符相同
        { 
            front++; //front后移
            ne[i] = front; //front++后,值正好为最长相同前后缀长度
            i++; //i后移
        }
        else //字符不相同
        {
            if(front == 0) //front在子串开头
            {
                ne[i] = 0; //最长相同前后缀长度为0
                i++; //i后移
            }
            else
                front = ne[front-1]; //修改front位置
        }
    }
}

2.3 实现

有了next数组,剩下的步骤也很简单:

当字符不匹配时

若j不在子串开头,则按照next[j-1]的数值修改j的位置,例如此处next[j-1]为2。某些情况中,在比较子串的第一个字符(j=0)时就已经不匹配了,此时不需要移动j,将i后移即可

于是将j移动到下标为2处,继续进行匹配。当字符匹配时,i和j各自后移即可

当j移动到子串结尾,说明子串匹配成功

2.4 例题

观察样例中可以发现,主串中可能存在多个与子串相同的字符串,我们需要把所有相同子串的起始位置都输出出来

#include <iostream>
#include <string>
#include <vector>
using namespace std;

const int N = 100010;
const int M = 1000010;

int ne[N]; //next数组
int n, m;

void buildNext(string &p) //构建next数组
{
    int front = 0;
    int i = 1;
    while(i < n)
    {
        if(p[i] == p[front])
        {
            front++;
            ne[i] = front;
            i++;
        }
        else
        {
            if(front == 0)
            {
                ne[i] = 0;
                i++;
            }
            else
                front = ne[front-1];
        }
    }
}

int main()
{
    string p;
    string s;
    cin >> n >> p >> m >> s;
    buildNext(p);
    vector<int> ans;
    int i = 0, j = 0;
    while(i < m) //当i没走到主串结尾时
    {
        if(s[i] == p[j]) i++, j++; //字符匹配,i、j后移
        else if(j > 0) j = ne[j-1]; //字符不匹配且j不在开头,根据ne数组修改位置
        else i++; //字符不匹配且j在开头,i后移
        if(j == n) //j走到子串结尾,说明子串匹配成功
        {
            ans.push_back(i-j); //放入i-j即子串在主串中的起始位置
            j = ne[j-1]; //修改j进行下一轮匹配
        }
    }
    for(auto i : ans)
    {
        cout << i << " ";
    }
    return 0;
}

完.

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

elementui时间选择器time-picker返回值不对的问题

1. 问题 天杀的elementui的time-picker&#xff0c;导致我开发的系统出现了一次生产问题&#xff0c;原因竟然是因为组件库的bug&#xff01;直接上截图。 如图&#xff0c;正常情况下&#xff0c;选择时间后&#xff0c;想要得到的值理应是当天的时间&#xff0c;如图是当年…

zotero文献管理学习

1 zotero软件简介 zotero是一款开源的文献管理软件。如果你听说或使用过EndNote&#xff0c;那么可能会对“文献管理”有一定的概念。可以简单地这样理解&#xff1a;zotero一定程度上可以作为EndNote的平替。 EndNote需要注册付费&#xff0c;对于无专业科研机构隶属关系的企…

使用apipost连接openai的接口进行模型对话

使用apipost连接openai的接口进行模型对话 1.API准备2.APIPOST配置2.1请求地址和header的设置2.2认证API设置2.3body设置2.4结果 1.API准备 这里使用网络上的API&#xff0c;使用硅基流动的 API Key&#xff0c;所以接下来便是注册并获取 API Key 了。 首先&#xff0c;我们打…

轻量级可视化数据分析报表,分组汇总表!

什么是可视化分组汇总表&#xff1f; 可视化分组汇总表&#xff0c;是一种结合了数据分组、聚合计算与视觉呈现功能的数据分析展示功能。它能够按照指定的维度&#xff08;如时间、地区、产品类型等&#xff09;对数据进行分组&#xff0c;还能自动计算各组的统计指标&#xf…

RabbitMQ 入门(四)SpringAMQP五种消息类型(Work Queue)

一、WorkQueue(工作消息队列) Work queues&#xff0c;也被称为&#xff08;Task queues&#xff09;&#xff0c;任务模型。简单来说就是让多个消费者绑定到一个队列&#xff0c;共同消费队列中的消息。 当消息处理比较耗时的时候&#xff0c;可能生产消息的速度会远远大于…

官龙村捐赠图书整理有感

今天&#xff08;2024年10月20日&#xff09;&#xff0c;我有幸参加了在深圳南山区西丽官龙村举行的义工活动&#xff0c;主要任务是整理捐赠的图书&#xff0c;并根据小学和中学的需求进行分类打包。这次活动不仅让我体会到了劳动的辛苦&#xff0c;更让我感受到了助人为乐的…

如何使用Python合并Excel文件中的多个Sheet

在日常工作中&#xff0c;我们经常会遇到需要处理多个Excel工作表&#xff08;Sheet&#xff09;的情况。比如&#xff0c;一个Excel文件中包含了一个月内每天的数据&#xff0c;每个工作表代表一天。有时候&#xff0c;为了方便分析&#xff0c;我们需要将这些分散的数据合并到…

【MySQL】详解MySQL数据类型

一、数据类型 各类型的数值范围&#xff1a; 在MySQL中&#xff0c;整型可以指定是有符号的和无符号的&#xff0c;默认是有符号的。 可以通过UNSIGNED来说明某个字段是无符号的。对于int类型可能存放不下的数据&#xff0c;尽量不使用unsigned&#xff0c;unsigned int 同样可…

国家信息安全水平考试(NISP一级)最新题库-第十六章

目录 另外免费为大家准备了刷题小程序和docx文档&#xff0c;有需要的可以私信获取 1 防火墙是一种较早使用、实用性很强的网络安全防御技术&#xff0c;以下关于防火墙说法错误的是&#xff08;&#xff09; A.防火墙阻挡对网络的非法访问和不安全数据的传递&#xff1b;B.防…

强对流降水临近预报

强对流降水是一种最常见的灾害性天气&#xff0c;其突发性和局地性强、生命史短、灾害重等特点极易给人民生产和生活带来巨大的破坏和伤害。如果可以提前预知此类天气状态&#xff0c;则可以挽回巨大的生命财产损失&#xff0c;尤其是短时&#xff08;0~12小时&#xff09;和临…

基础篇:带你打开Vue的大门(二)

目录 学习目标&#xff1a; 核心技能目标 学习内容&#xff1a; 学习产出&#xff1a; 学习目标&#xff1a; 能够创建Vue实例并理解其基本选项。 理解el、data、methods等选项的作用。 掌握数据绑定&#xff1a; 理解单向数据绑定和双向数据绑定的区别。能够使用v-bind和…

MySQL进阶之(十二)MySQL事务日志-undo log

十二、MySQL事务日志-undo log 12.1 undo log 引入12.2 undo log 的作用01、回滚数据02、MVCC 12.3 undo log 的存储结构01、回滚段与 undo 页02、回滚段与事务03、回滚段中的数据分类 12.4 undo log 的类型12.5 undo log 的生命周期01、执行 insert 操作02、执行 update 操作0…

Kubernetes部署练习

Kubernetes详细笔记 文章目录 Kubernetes 一、Kubernetes介绍 1.1、应用部署方式演变1.2、kubernetes简介1.3、kubernetes组件1.4、kubernetes概念 二、集群环境搭建 2.1、环境规划 2.1.1、集群类型2.1.2、安装方式2.1.3、主机规划 2.2、环境搭建 2.2.1、主机安装2.2.2、环境初…

如何开启华为交换机 http

系列文章目录 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 例如&#xff1a;第一章 Python 机器学习入门之pandas的使用 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目…

【计算机网络 - 基础问题】每日 3 题(四十七)

✍个人博客&#xff1a;https://blog.csdn.net/Newin2020?typeblog &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/fYaBd &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞…

极速体验:实用的前端性能优化技巧

本文将深入探讨一系列实用的前端性能优化方案&#xff0c;从基础知识到高级技巧&#xff0c;我们将揭示如何让你的网站在瞬息万变的互联网中脱颖而出&#xff0c;无论你是经验丰富的开发者还是刚入行的新手&#xff0c;这篇文章都将为你提供宝贵的见解和实践建议。 目录 &…

python解决解析汉诺塔问题

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…

如何选择适合自己的IP地址切换器

在互联网活动中&#xff0c;IP地址切换器成为越来越多用户的必备工具。本文旨在帮助用户了解如何选择适合自己的IP地址切换器&#xff0c;以确保网络活动的顺利进行。 ​一、IP地址切换器的基本功能与优势 IP地址切换器&#xff0c;又称IP代理软件或IP更换器&#xff0c;是一种…

计算机通信与网络实验笔记

1.LINUX通过版本号判断是否为稳定版本 2.计网基础 &#xff08;CD&#xff09;&#xff0c;默认二层以太网交换机。 &#xff08;10&#xff09;物理层是均分&#xff08;除以&#xff09;&#xff0c;数据链路层及以上是不除的。 3.传输介质&#xff1a; &#xff08;1&…

IDEA 中的代码调试指南

目录 前言1. 为什么进行代码调试1.1 找出错误1.2 优化代码1.3 提高对代码的理解 2. 如何在 IDEA 中进行代码调试2.1 设置断点2.1.1 普通断点2.1.2 条件断点 2.2 开始调试2.3 调试控制2.3.1 单步调试&#xff08;Step Over&#xff09;2.3.2 进入方法&#xff08;Step Into&…