较难理解的字符串查找算法KMP

news2024/11/15 8:26:38

时间复杂度O(n)的子串查找算法。


经典实例


主字符串(s):abcabcabd
模式串(t):abcabd

比较次数    主字符串    模式串    备注
一    abcabcabd    abcabd    红色和绿色表示正在比较的子串,红色表示不同部分,绿色表示相同部分。
二    abcabcabd    abcabd    
三    abcabcabd    abcabd    
四    abcabcabd    abcabd    
五    abcabcabd    abcabd    
六    abcabcabd    abcabd    ab是abcab的公共前后缀,abcab是上次(第五次比较成功的子串)
七    bcab    abca    
八      cab    abc    
九      ab    ab    

观点:只需要比较上次相等部分的公共前后缀


假定一:s[i1...i2)等于t[0...j)
假定二:s[i2]不等于t[j]
假定二的意思是i1不是find的返回值。
假定三:x取[i1...i2),字符串s[x...i2)的长度是len=i2-x。
假定一和假定三可以得出推理一:s[x,i2)等于t[j-len...j)。
结合推理一,如果t[0...j-len)不等于t[j-len,j)则t[0...j-len)不等于s[x...i2),也就是s[x...]不是find返回值。
结论一:如果t[0...j)长度为len的前缀和后缀不相等,则x不是结果,直接忽略。
结论二:如果t[0...j)长度为len的前缀和后缀相等,则t[x...j)和s[0...len)相等,直接比较t[j]和s[len)。
从最长公共前缀处理还是从最短公共前缀开始
i1递增的过程和从最长公共前缀到最短公共前缀的过程。


不需要记录所有公共前缀


只需要记录最长公共前缀,然后递归或迭代求。因为:次长公共前缀就是最长公共前缀的最长公共前缀。


说明


s[i,j)表示从i到j的子串,包括i不包括j。S[x...]表示从索引k开始的子串,长度未定。
字符串s[0,j)公共前后缀指的是s[0,x)等于s[j-x,j),x不等于j,也就是公共前后缀必能是本身。
获取最长公共前后缀
如果s[0,j)的公共前缀为x,如果x大于0,则必定有s[0,j-1)的前缀为x-1。所以只需要比较s[0,j-1)的公共前后缀。

核心代码

class KMP
{
public:
    virtual int Find(const string& s,const string& t )
    {
        CalLen(t);
        m_vSameLen.assign(s.length(), 0);
        for (int i1 = 0,  j = 0; i1 < s.length(); )
        {
            for (; (j < t.length()) && (i1 + j < s.length()) && (s[i1 + j] == t[j]); j++);
            //i2 = i1 + j 此时s[i1,i2)和t[0,j)相等 s[i2]和t[j]不存在或相等
            m_vSameLen[i1] = j;
            //t[0,j)的结尾索引是j-1,所以最长公共前缀为m_vLen[j-1],简写为y 则t[0,y)等于t[j-y,j)等于s[i2-y,i2)
            if (0 == j)
            {
                i1++;
                continue;
            }
            const int i2 = i1 + j;
            j = m_vLen[j - 1];
            i1 = i2 - j;//i2不变
        }
        
        for (int i = 0; i < m_vSameLen.size(); i++)
        {//多余代码是为了增加可测试性
            if (t.length() == m_vSameLen[i])
            {
                return i;
            }
        }
        return -1;
    }
protected:
    void CalLen(const string& str)
    {
        m_vLen.resize(str.length());
        for (int i = 1; i < str.length(); i++)
        {
            int next = m_vLen[i-1];
            while (str[next] != str[i])
            {
                if (0 == next)
                {
                    break;
                }
                next = m_vLen[0];
            }
            m_vLen[i] = next + (str[next] == str[i]);
        }
    }
    int m_c;
    vector<int> m_vLen;//m_vLen[i] 表示t[0,i]的最长公共前后缀
    vector<int> m_vSameLen;//m_vSame[i]记录 s[i...]和t[0...]最长公共前缀,增加可调试性
};

测试代码


class CTestKMP :public KMP
{
public:
    virtual int Find(const string& s, const string& t) override
    {
        int iRet = KMP::Find(s,t);
        for (int i = 0; i < m_vLen.size(); i++)
        {
            std::cout << t.substr(0, i + 1).c_str() << " " << m_vLen[i] << std::endl;
        }
        return iRet;
    }
    void Assert(const vector<int>& vLen,const vector<int>& vSameLen)
    {
        for (int i = 0; i < vLen.size(); i++)
        {
            assert(vLen[i] == m_vLen[i]);
        }
        for(int i = 0 ; i < vSameLen.size();i++)
        {
            assert(vSameLen[i] == m_vSameLen[i]);
        }
    }
};

int main()
{
    vector<string> ss = { "abcabcabd","abc","abcb","cabaab"};
    vector<string> ts = { "abcabd" ,"d","b","ab"};
    vector<vector<int>> lens = { {0, 0, 0, 1, 2, 0},{0},{0},{0,0} };
    vector<vector<int>> sameLens = { {5, 0, 0, 6, 0, 0,0,0,0},{0,0,0},{0,1,0,1},{0,2,0,1,2,0 } };
    for (int i = 0; i < ss.size(); i++)
    {
        CTestKMP kmp;
        auto res = kmp.Find(ss[i], ts[i]);
        kmp.Assert(lens[i], sameLens[i]);
    }
    
}

开发测试环境

Win10 VS2022

如果格式混乱影响阅读,请到CSDN下载word版。目前审核中,请稍候!

作者的话

KMP确实比较难理解,我学习了多次。并且重写了至少两次。希望这次是真懂了。

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

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

相关文章

知识联合——函数指针数组

前言&#xff1a;小伙伴们又见面啦&#xff0c;今天我们来讲解一个将函数&#xff0c;指针&#xff0c;数组这三个C语言大将整合在一起的知识——函数指针数组。同时来告诉小伙伴们我们上一篇文章的伏笔——函数指针的具体用法。 目录 一.什么是函数指针数组 二.函数指针数组…

【Java】全套云HIS(医院信息管理系统)源码包含EMR、LIS

云HIS系统简介 SaaS模式Java版云HIS系统源码&#xff0c;在公立二甲医院应用三年&#xff0c;经过多年持续优化和打磨&#xff0c;系统运行稳定、功能齐全&#xff0c;界面布局合理、操作简便。 1、融合B/S版电子病历系统&#xff0c;支持电子病历四级&#xff0c;HIS与电子病…

linux下centos7升级python版本

由于项目需要使用爬虫&#xff0c;爬虫框架支撑3.8以上版本。而linux自带的python版本是2.7.*&#xff0c;所以需要升级python版本至3.8 键脚本安装Python3.6-Python3.10 bash <(curl -sSL https://raw.githubusercontent.com/midoks/choose-linux-python/main/install.sh…

MySql入门到精通,优化,

Mysql概述 1.数据库相关概念 1.数据库&#xff1a;数据存储的仓库 2.数据库管理系统&#xff1a;操作和管理数据库的大型软件。&#xff08;DBMS&#xff09; 3.SQL&#xff1a;操作关系型数据库的编程语言&#xff0c;是一套标准。 都要使用SQL操作语言&#xff08;统一标准…

左神高级进阶班3(TreeMap顺序表记录线性数据的使用, 滑动窗口的使用,前缀和记录结构, 可能性的舍弃)

目录 【案例1】 【题目描述】 【思路解析】 【代码实现】 【案例2】 【题目描述】 【思路解析】 【代码实现】 【案例3】 【题目描述】 【思路解析】 【代码实现】 【案例4】 【题目描述】 【思路解析】 【代码实现】 【案例1】 【题目描述】 【思路解析】 这里…

vue-cli init vue3

安装 cli npm i -g vue/cli4.5.13查看版本&#xff1a;vue -V升级版本&#xff1a;npm update -g vue/cli 初始化项目 vue create 项目名称 > - ? Please pick a preset:Default ([Vue 2] babel, eslint)Default (Vue 3) ([Vue 3] babel, eslint) > Manually selec…

什么是HTML5中的Web存储API,包括LocalStorage和SessionStorage?它们的区别是什么?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ Web存储API和区别⭐ LocalStorage&#xff08;本地存储&#xff09;⭐ SessionStorage&#xff08;会话存储&#xff09;⭐ 区别⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff0…

HTTP状态码301(永久重定向)不同Web服务器的配置方法

文章目录 301状态码通常在那些情况下使用301永久重定向配置Nginx配置301永久重定向Windows配置IIS301永久重定向PHP下的301重定向Apache服务器实现301重定向 301重定向是否违反相关法规&#xff1f;推荐阅读 当用户或搜索引擎向服务器发出浏览请求时&#xff0c;服务器返回的HT…

腾讯轻联:带你创造属于自己的AI小助手

陈老老老板&#x1f934; &#x1f9d9;‍♂️本文专栏&#xff1a;生活&#xff08;主要讲一下自己生活相关的内容&#xff09;生活就像海洋,只有意志坚强的人,才能到达彼岸。 &#x1f9d9;‍♂️本文简述&#xff1a;参加腾讯全球数字生态大会&#xff0c;了解到腾讯轻联企业…

LLM(一)| 百川智能baichuan7B、13B、53B以及baichuan2总结

之前在文章baichuan-53B VS ChatGLM-6B对比中做过百川大模型53B和ChatGLM 6B模型的效果对比&#xff0c;由于百川大模型的内测模型是53B&#xff0c;因此本次对比参数量差异较大&#xff0c;但仍然可以看到两个模型的效果。百川大模型在benchmark上有超越ChatGLM和LLaMA的迹象&…

《DevOps实践指南》- 读书笔记(六)

DevOps实践指南 Part 4 第二步 &#xff1a;反馈的技术实践17. 将假设驱动的开发和A/B测试融入日常工作17.1 A/B 测试简史17.2 在功能测试中集成 A/B 测试17.3 在发布中集成 A/B 测试17.4 在功能规划中集成 A/B 测试17.5 小结 18. 建立评审和协作流程以提升当前工作的质量18.1 …

内存映射-

内存映射区不会堵塞 父子进程之间通信 /*#include <sys/mman.h>void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);作用&#xff1a;将一个文件或者设备的数据映射到内存中参数&#xff1a;- addr&#xff1a;NULL- length&#xff1…

计算机毕设 flink大数据淘宝用户行为数据实时分析与可视化

文章目录 0 前言1、环境准备1.1 flink 下载相关 jar 包1.2 生成 kafka 数据1.3 开发前的三个小 tip 2、flink-sql 客户端编写运行 sql2.1 创建 kafka 数据源表2.2 指标统计&#xff1a;每小时成交量2.2.1 创建 es 结果表&#xff0c; 存放每小时的成交量2.2.2 执行 sql &#x…

双喜临门 | 开源网安SFuzz在谷歌FuzzBench中夺魁,并获得CWE国际认证

​近日&#xff0c;开源网安模糊测试平台SFuzz于上半年推出的“灰盒代码模糊测试系统”&#xff0c;经过数月的不断调优&#xff0c;在谷歌的FuzzBench基准测试中脱颖而出&#xff0c;荣登榜首。此外&#xff0c;SFuzz还成功获得了CWE国际兼容性认证&#xff0c;进一步巩固了其…

警报:新的 Kubernetes 漏洞可对 Windows 端点实施远程攻击

不久前&#xff0c;研究人员在 Kubernetes 中发现的三个可被利用并相互关联的高危安全漏洞&#xff0c;这些漏洞可在集群内的 Windows 端点上以提升权限的方式实现远程代码执行。 这些漏洞被标记为 CVE-2023-3676、CVE-2023-3893 和 CVE-2023-3955&#xff0c;CVSS 评分为 8.8…

第73步 时间序列建模实战:多步滚动预测 vol-1(以决策树回归为例)

基于WIN10的64位系统演示 一、写在前面 上一期&#xff0c;我们讲了单步滚动预测&#xff0c;一次只预测一个值。 既然有单步&#xff0c;有没有多步呢&#xff1f;那肯定有&#xff0c;这一期来介绍多步滚动预测。 然而&#xff0c;多步滚动模型也可以有不同的步骤&#x…

Mac电脑运行太卡怎么办?

许多小伙伴使用Mac后都反馈电脑不如想象中的流畅&#xff0c;甚至有点卡顿的现象&#xff0c;原因可能是因为无用的应用占据了过多的内存&#xff0c;或者是系统盘垃圾过多&#xff0c;导致的电脑卡顿现象。 今天小编教给大家几招&#xff0c;让自己的Mac能够一键重生&#xf…

创造性地解决冲突

1、冲突的根本原因是矛盾双方存在不可调和的目标冲突。 2、要知己知彼&#xff1a; 知己&#xff1a;就是对自己的问题、需求进行客观定义&#xff0c;说明需求和问题的意义或价值、阐述解决方案和期望效果&#xff1b; 知彼&#xff1a;站在对方立场&#xff0c;深挖对方真…

探索AIGC人工智能(Midjourney篇)(四)

文章目录 Midjourney模特换装 Midjourney制作APP图标 Midjourney网页设计 Midjourney如何生成IP盲盒 Midjourney设计儿童节海报 Midjourney制作商用矢量插画 Midjourney设计徽章 Midjourney图片融合 Midjourney后缀参数 Midjourney模特换装 关键词生成模特照片 中国女性模特的…

虚拟机Ubuntu操作系统常用终端命令(3)(详细解释+详细演示)

本篇概要 本篇讲述了Ubuntu操作系统常用的几个功能&#xff0c;即修改文件权限、修改文件属性、可执行脚本、虚拟机网络、FTP服务器、SSH服务器、VIM等方面的知识。希望能够得到大家的支持。 文章目录 本篇概要1.修改文件权限2.修改文件属主3.可执行脚本3.1要点与细节3.2shell…