字符串匹配——KMP算法

本文深入解析KMP算法,一种高效的字符串匹配算法,介绍其工作原理、next数组的概念及其实现方法,通过图表清晰展示算法执行过程,附带C++代码示例。

转载记录学习,原文:https://www.cnblogs.com/yusenwu/p/4782043.html

      KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现,因此人们称它为克努特——莫里斯——普拉特操作(简称KMP算法)。KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是实现一个next()函数,函数本身包含了模式串的局部匹配信息。

  原理如果文字理解起来非常复杂,而且有点难懂。因此,画图来讲解是最好的方式啦,下图非常容易理解算法的执行原理。

     我之前也是看了这幅图理解的。所以我觉得把这个图用来讲解最好不过(抱歉,我搬了这图,但是这个图是我至今觉得讲的最好的图,不得不搬),当然我已经全部重新画过。网络上KMP的讲解,讲的好的寥寥无几,发现一些博客,都是转载,或者讲的不够清楚,很难理解。我觉得有必要重新整理整理,自己来梳理一下知识点,为了让自己更理解深刻一些。

  首先模式串逐一对比文本串,如上图,直到遇到相同的元素,如下图:

   模式串,逐一对比,直到发现蓝色框框内的字符不相同,下图。这时候怎么办?

   BS算法,就是把模式串向前移动一位,从头继续比较,所以他的时间复杂度最差才是o(m*n)。而KMP呢,不再从头比较啦,这样大大减少了时间复杂度。我们即将引出next数组概念。

  既然,不保存,那他是怎么跳的呢?

       我们发现,ABCDAB,AB**AB, 这个字符串首尾相同,因此直接跳4格,如下图。

  

       也就是说,next数组保存的数和跳几格是有关系的呗。那我们怎么来看呢?这个字符串的匹配值有关。我们只要数,字符串首尾有几个是匹配的即可,通过这样来初始化。我们来看一下这个表格。

        A = 0  AB = 0  ABC = 0  ABCD = 0  ABCDA = 1  ABCDAB = 2  ABCDABD = 0

  公式:移动位数 = 已匹配的字符数 - 表格内的匹配值

   我们继续看,即使跳转了4格,还是有蓝色的部分不匹配,又因为AB = 0 所以移动位数 = 已匹配的字符数(2) - 表格内的匹配值(0) = 2,依次类推,直到匹配到下图,则成功。

  该算法,最重要的是next数组上。理解这个,我们觉得其他就迎刃而解了。

2、代码实现

主要代码(c++版):

std::map<int,int> compute_prefix(const std::string &pattern)
{
    int i = 1;
    int p = 0;
    std::map<int, int> pi;
    int length = pattern.length();
    pi.insert(std::make_pair(1, 0));
    while (i < length)
    {
        if (p > 0 && pattern[i] != pattern[p])
        {
            p = 0;
        }
        if (pattern[i] == pattern[p])
        {
            ++p;
        }
        pi.insert(std::make_pair(i + 1, p));
        i++;
    }
    return pi;
}

bool kmp_match(const std::string &text,const std::string &pattern)
{
    std::map<int, int> pos;
    pos = compute_prefix(pattern);
    int q = 0;
    for (int i = 0; i < text.length(); i++)
    {
        if (q > 0 && text[i] != pattern[q])
        {
            q = pos.at(q);
        }
        if (text[i] == pattern[q])
        {
            q++;
        }
        if (q == pattern.length())
        {
            return true;
        }
    }
}

测试代码:

int main()
{
    char a[] = "bbc abcdab abcdabcdabde";
    char b[] = "abcdabd";

    bool iftrue = kmp_match(a, b);;
    if (iftrue == true)
    {
        std::cout << "找到了" << std::endl;
    }
    else
    {
        std::cout << "没有" << std::endl;
    }
}

完整代码:

#include <iostream>
#include <map>
#include <string>
#include <utility>
#include <stdlib.h>

std::map<int,int> compute_prefix(const std::string &pattern)
{
    int i = 1;
    int p = 0;
    std::map<int, int> pi;
    int length = pattern.length();
    pi.insert(std::make_pair(1, 0));
    while (i < length)
    {
        if (p > 0 && pattern[i] != pattern[p])
        {
            p = 0;
        }
        if (pattern[i] == pattern[p])
        {
            ++p;
        }
        pi.insert(std::make_pair(i + 1, p));
        i++;
    }
    return pi;
}

int kmp_match(const std::string &text,const std::string &pattern)
{
    std::map<int, int> pos;
    pos = compute_prefix(pattern);
    int q = 0;
    for (int i = 0; i < text.length(); i++)
    {
        if (q > 0 && text[i] != pattern[q])
        {
            q = pos.at(q);
        }
        if (text[i] == pattern[q])
        {
            q++;
        }
        if (q == pattern.length())
        {
            return (i+1)-q+1;
        }
    }
    return -1;
}

int main()
{
    char a[] = "bbc abcdab abcdabcdabde";
    char b[] = "abcdabd";

    int iftrue = kmp_match(a, b);;
    if (iftrue >=0)
    {
        std::cout << "找到了" << " "<< iftrue <<std::endl;
    }
    else
    {
        std::cout << "没有" << std::endl;
    }
    system("pause");
}

完整代码

一个计算例子:

源码地址: https://pan.quark.cn/s/d1f41682e390 miyoubiAuto 米游社每日米游币自动化Python脚本(务必使用Python3) 8更新:更换cookie的获取地址 注意:禁止在B站、贴吧、或各大论坛大肆传播! 作者已退游,项目不维护了。 如果有能力的可以pr修复。 小引一波 推荐关注几个非常可爱有趣的女孩! 欢迎B站搜索: @嘉然今天吃什么 @向晚大魔王 @乃琳Queen @贝拉kira 第三方库 食用方法 下载源码 在Global.py中设置米游社Cookie 运行myb.py 本地第一次运行时会自动生产一个文件储存cookie,请勿删除 当前仅支持单个账号! 获取Cookie方法 浏览器无痕模式打开 http://user.mihoyo.com/ ,登录账号 按,打开,找到并点击 按刷新页面,按下图复制 Cookie: How to get mys cookie 当触发时,可尝试按关闭,然后再次刷新页面,最后复制 Cookie。 也可以使用另一种方法: 复制代码 浏览器无痕模式打开 http://user.mihoyo.com/ ,登录账号 按,打开,找到并点击 控制台粘贴代码并运行,获得类似的输出信息 部分即为所需复制的 Cookie,点击确定复制 部署方法--腾讯云函数版(推荐! ) 下载项目源码和压缩包 进入项目文件夹打开命令行执行以下命令 xxxxxxx为通过上面方式或取得米游社cookie 一定要用双引号包裹!! 例如: png 复制返回内容(包括括号) 例如: QQ截图20210505031552.png 登录腾讯云函数官网 选择函数服务-新建-自定义创建 函数名称随意-地区随意-运行环境Python3....
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值