KMP算法详解(参考代码随想录)

2023-11-14

KMP算法详解(参考代码随想录)

KMP的经典思想:当出现字符串不匹配时,可以记录一部分之前已经匹配的文本内容,利用这些信息避免从头再去做匹配。

前缀表

前缀表是用来回退的,它记录了模式串与主串(文本串)不匹配的时候,模式串应该从哪里开始重新匹配。

next数组就是一个前缀表(prefix table):记录下标i之前(包括i)的字符串中,有多大长度的相同前缀后缀。

最长公共(相等)前后缀

前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串。

后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串。

因为前缀表要求的就是相同前后缀的长度。

为什么使用前缀表

因为当模式串与文本串匹配失败时,当前匹配失败的字符串不包含当前字符的后缀与文本是匹配的,因此通过找到与后缀相等的前缀就可以跳过对前缀的匹配判断。

img

如何计算前缀表

模式串:aabaaf

a,最长相等前后缀长度:0

aa,最长相等前后缀长度:1,最长相等前后缀:a

aab,最长相等前后缀长度:0

aaba,最长相等前后缀长度:1,最长相等前后缀:a

aabaa,最长相等前后缀长度:2,最长相等前后缀:aa

aabaaf,最长相等前后缀长度:0

a a b a a f
0 1 0 1 2 0

前缀表与next数组

next数组就可以是前缀表,但是很多实现都是把前缀表统一减一(右移一位,初始位置为-1)

使用next数组来匹配

时间复杂度分析

匹配过程O(n),生成next数组O(m)

总时间复杂度O(n+m)

构造next数组

构造next数组==计算前缀表,主要有以下三步:

  1. 初始化
  2. 处理前后缀不相同的情况
  3. 处理前后缀相同的情况

1. 初始化

两个指针:i指向后缀末尾位置,j指向前缀末尾位置

next数组初始化赋值:

int j=0;
next[0]=j;

其中,j初始化为0,i初始化为1。

由于j指向前缀末尾位置,因此j+1是当前前缀的长度,也就是前后缀相等的长度。

其中,next[i]存放的是i之前(包括i)最长相等前后缀长度。

1. 为什么i初始化为1?

原因:后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串,i=0的位置是该字符串的第一个字符,因此指向后缀末尾位置的i不能从0开始。

2. 为什么j+1是前后缀相等的长度?

原因:当j指向的元素与i指向的元素匹配时,在j指向的元素之前的每个元素(包括j所指的元素)都与i指向的元素之前的每个元素(包括i所指的元素)匹配,即j所表示前缀与i所表示的后缀匹配。因此,j的位置能代表前后缀相等的长度。由于j是下标,且从0开始,因此长度需要加一,即j+1

2. 处理前后缀相同的情况

s[i]==s[j]时,说明找到了相同的前后缀,将当前相等前后缀的长度j++之后的j,即j+1,赋值给next[i]数组。ij分别往后移动一位,比较下一个元素,jj++中移动,i在循环条件中移动。

if (s[i] == s[j]) {
	j++;
}
next[i] = j;

3. 处理前后缀不相同的情况

s[i]!=s[j]时,说明当前前后缀的末尾不同,因此j向前回退j=next[j-1];

1. 为什么只比较前缀末尾j和后缀末尾i单个元素就能判断前后缀是否相同?

原因:

  • i在扫描过程中相当于隐性地存下了后缀(从下标1位置到下标i位置都是当前所指字符串的后缀)
  • j能够向后移动,说明j的前面与i的前面一定是匹配的,且匹配的元素个数是j+1个,即i前的j+1个元素匹配。
  • 因此,表面上只比较了一个元素,实际上在移动过程中,前面的元素都被比较过。

2. 回退的逻辑?

  • 回退的逻辑本质上与模式串和文本串匹配逻辑相同
  • 回退是在寻找j所表示的字符串中的最长相等前后缀,当s[i]!=s[j]时,0~j-1所表示的j个元素构成的字符串1与i-j~i-1所表示的j个元素构成的字符串2是匹配的。因此,我们只需要找到字符串1中的最长相等前后缀,即next[j-1],就能跳过前缀的重新匹配(因为在最长相等前后缀中,前缀与后缀是相同的,i前部分元素与后缀相同,因此前缀和i前部分元素相同,所以不用再匹配)

img

img

前缀表的代码实现

void getNext(int* next, const string& s) {
    int j = 0;
    next[0] = 0;
    for(int i = 1; i < s.size(); i++) {
        while (j > 0 && s[i] != s[j]) { // j要保证大于0,因为下面有取j-1作为数组下标的操作
            j = next[j - 1]; // 注意这里,是要找前一位的对应的回退位置了
        }
        if (s[i] == s[j]) {
            j++;
        }
        next[i] = j;
    }
}

使用next数组来做匹配

在文本串s中寻找是否出现过模式串

定义下标j指向模式串起始位置,i指向文本串起始位置,其中,j=0;i=0;

如果s[i]与t[j]不相同,j从next数组中寻找下一个匹配位置

while(j > 0 && s[i] != t[j]) {
    j = next[j-1];
}

如果s[i]与t[j]相同,那么i j同时向后移动

if (s[i] == t[j]) {
    j++; // i的增加在for循环里
}

判断在文本串s里出现了模式串t:

如果j指向了模式串t的末尾,那么就说明模式串t完全匹配文本串s里的某个子串了。

如果要在文本串字符串中找出模式串出现的第一个位置 (从0开始),所以返回当前在文本串匹配模式串的位置i 减去 模式串的长度,就是文本串字符串中出现模式串的第一个位置。

if (j == t.size()) {
    return (i - t.size() + 1);
}

前缀表(不减一)的实现

class Solution {
public:
    void getNext(int* next, const string& s) {
        int j = 0;
        next[0] = 0;
        for(int i = 1; i < s.size(); i++) {
            while (j > 0 && s[i] != s[j]) {
                j = next[j - 1];
            }
            if (s[i] == s[j]) {
                j++;
            }
            next[i] = j;
        }
    }
    int strStr(string haystack, string needle) {
        if (needle.size() == 0) {
            return 0;
        }
        int next[needle.size()];
        getNext(next, needle);
        int j = 0;
        for (int i = 0; i < haystack.size(); i++) {
            while(j > 0 && haystack[i] != needle[j]) {
                j = next[j - 1];
            }
            if (haystack[i] == needle[j]) {
                j++;
            }
            if (j == needle.size() ) {
                return (i - needle.size() + 1);
            }
        }
        return -1;
    }
};
f (haystack[i] == needle[j]) {
                j++;
            }
            if (j == needle.size() ) {
                return (i - needle.size() + 1);
            }
        }
        return -1;
    }
};
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

KMP算法详解(参考代码随想录) 的相关文章

随机推荐

  • 搭建AI智能语音外呼系统 智能语音外呼机器人

    随着人工智能技术的发展 近半年来涌现了大量基于人工智能的呼叫中心业务服务商和集成商 仅电销机器人这一个方向就至少有近百家公司正在推广运营 包括百度 讯飞 智齿 硅基 百应 箭鱼 容联等 商务上的需求非常强烈 整个市场都飞快地热闹起来 一套可
  • 小细节{变量名-枚举}

    一 类的变量名第一个字母一定要小写 eventType event type eventId 13 userId 45 openingFlag true Data TableName user activity AllArgsConstru
  • 基于matlab的车牌识别

    20221126 新增 首先说一下这个工程的思路 很多朋友妄想直接拿着工程用 那是不可能的 自己学去叭 我是先将车牌号预处理之后 整个图片干净一点之后 进行每个字符的切割 但是是很投机取巧的方法 是先切好第一个字符 再根据切割坐标 切割下一
  • 堆排序算法的具体分析和实现

    定义 堆就是完全二叉树的数据结构 堆排序是利用二叉树的孩子与双亲节点的比较来实现的排序方法 大顶堆 每个节点的值都大于或者等于它的左右子节点的值 小顶堆 每个节点的值都小于或者等于它的左右子节点的值 这里使用的是大顶堆 基本思想 堆排序的基
  • Meta 内部都在用的 FX 工具大起底:利用 Graph Transformation 优化 PyTorch 模型

    PyTorch 中的 graph mode 在性能方面表示更为出色 本文介绍 Torch FX 这个强大工具 可以捕捉和优化 PyTorch 程序 graph 一 简介 PyTorch 支持两种执行模式 eager mode 和 graph
  • 用Ai描摹图片

    用Ai描摹图片 陈子龙 2019 2 4 用ai来描摹这张图片 先用钢笔工具把哆啦A梦的外面黑的地方钩画出来 并上色 然后在把哆啦A梦的身体蓝色的地方用钢笔描出来 在把它白色的部位用钢笔描出
  • C语言中堆内存的申请和使用

    在编程过程中 有时需要使用大量数据 此时可以使用堆内存来方便存储和管理这些数据 堆内存是由程序员手动进行申请 释放的内存 它的空间非常大 但如果在申请后没有释放 会导致内存泄露 关于堆内存的常用函数 1 void malloc size t
  • 一文读懂微服务架构设计

    一 前言 微服务 MicroServices 是一种架构风格 一个大型复杂软件应用由多个微服务和前端展示层组成 系统中的各个微服务可被独立部署 各个微服务之间是松耦合的 每个微服务仅关注于完成一件任务并很好地完成该任务 在所有情况下 每个任
  • 组合预测模型

    组合预测模型 ARIMA CNN LSTM时间序列预测 Python 目录 组合预测模型 ARIMA CNN LSTM时间序列预测 Python 预测结果 基本介绍 程序设计 参考资料 预测结果 基本介绍 ARIMA CNN LSTM是一种
  • Django运行服务报NameError: name ‘os‘ is not defined-已解决

    这里调用了os模块 但是文件头并没引用os模块 解决办法 在settings py文件头加上 import os
  • 【MySQL】解决JDBC无法成功连接MySQL5.7的问题

    写在前面 笔者的个人主页近期升级了一下服务器 以前的VPS确实不行了 然后也就顺便用了最新版本也就是MySQL5 7 但是这个版本呢升级了很多安全策略 网上的资料 中文 也相对较少 之前因为安装这个MySQL5 7已经折腾了我大半天 这里附
  • CSS深入理解之line-height

    以下文字整理自慕课网 张鑫旭的 CSS深入理解之line height 我看到不时有人点赞收藏这篇文章 我想应该也有很多人是对line height 和vertical align 困惑吧 你们可以去看下这篇文章 上面有我学习vertica
  • texstudio更新记录

    Ubuntu20 04 更新TexStudio 本着不折腾不舒服的原则 准备更新texstudio 原版本2 12 22 texstudio网站上是没找到Ubuntu的 只有xubuntu版本的安装包 既然推荐用ppa方式 那就试试 点开紫
  • Windows Server间文件实时备份(syncthing) ---带历史版本“后悔药”

    一 概念简介 syncthing 一款开源免费的数据同步工具 基于P2P的跨平台文件同步工具 通过tcp建立设备连接 再通过TLS进行数据安全传输 支持公网与局域网搭建 支持单双向同步与历史版本控制 后悔药 支持Android Linux
  • go 进阶 三方库之 gorm

    目录 一 初始化 二 增删改查示例 Save与Update区别 GORM中的钩子 GORM Context支持 GORM 与锁 GORM的预加载Preload与Joins 查询时优雅的处理动态条件 分页 gorm io plugin扩展包
  • 【DFS】1905. 统计子岛屿

    1905 统计子岛屿 解题思路 如果两个岛屿的点不一样 说明grid2这个岛屿一定不是子岛屿 然后淹没i j 以及相邻的土地 现在grid2 剩下的岛屿 全部都是子岛屿 计算岛屿的数量 dfs计算陆地数量 class Solution pu
  • java类本身自己,如何在数据库中使用自己的 Java 类?

    如何在数据库中使用自己的 Java 类 Java 语言比 SQL 更强大 Java 是一种面向对象的语言 因此它的指令 源代码 采用类的形式 要在数据库中执行 Java 应在数据库外编写 Java 指令并在数据库外将它们编译为已编译的类 字
  • 2022秋招笔试加面经合集,不区分公司,不定期更新

    9 9日mark 秋招陆陆续续开始 我自己的定位首先是国企然后是互联网企业 这里把面试和笔试整理下 攒人品 废话不多说开始 首先说一下简历吧 很多同学可能投后台 测试 算法都是一个简历 这样对自己来说是很方便 但是用通用的简历就会导致面试官
  • keil 4单片机程序的debug调试

    1 单击keil4窗口的调试按钮快捷图标 进入到软件模拟调试模式 如图所示 在软件调试模式下 可以设置断点 单步 全速 进入某个函数内部运行 还可以查看变量的变化过程 模拟硬件IO口电平变化 查看代码执行时间等 先了解一下调试按钮的功能 其
  • KMP算法详解(参考代码随想录)

    KMP算法详解 参考代码随想录 KMP的经典思想 当出现字符串不匹配时 可以记录一部分之前已经匹配的文本内容 利用这些信息避免从头再去做匹配 前缀表 前缀表是用来回退的 它记录了模式串与主串 文本串 不匹配的时候 模式串应该从哪里开始重新匹