当前位置: 首页 > news >正文

KMP算法

KMP算法

基本概念

KMP算法是一种高效的字符串匹配算法,算法名称取自于三位共同发明人名字的首字母组合。该算法的主要使用场景就是在字符串(也叫主串)中的模式串(也叫子串)定位问题,常见的有“求子串出现的起始位置”、“求子串的出现次数”等。

核心算法

暴力匹配

暴力匹配的算法思想很简单,即依次枚举主串中的字符,作为匹配子串的起始字符,然后将两个字符串从起始位置一一比对,若中间出现字符串不匹配的情况,则将主串的起始位置回溯到上一起始位置的下个位置,子串的起始位置回溯到第一个字符,重新开始匹配,时间复杂度为O(m*n),效率较低

在这里插入图片描述

算法优化

KMP算法相比于暴力匹配算法,最大的优化就是利用匹配失败时失败之前的已知部分时匹配的这个有效信息,保持主串的 i 指针不回溯,通过修改模式串(子串)的 j 指针,使模式串尽量地移动到有效的匹配位置该算法的时间复杂度为 O(n+m),算法过程示例如下:

在这里插入图片描述

对上面的例子进行抽象分析:

在这里插入图片描述

其中,我们需要借助next数组,来求出子串的最大相等前缀和后缀的长度,即上图中的B,当主串与子串的字符不匹配时,我们只需要让子串的起始位置回溯到最长相等前缀的下一个位置即图中的z,而主串不需要再回溯,这样就大大提高了匹配效率,时间复杂度也减小到了O(m+n)

next数组

next数组的定义
  • 最长前缀概念: 最长前缀是说以第一个字符开始,但是不包含最后一个字符
  • 最长后缀概念: 最长后缀是说以最后一个字符开始,但是不包含第一个字符
  • next 数组定义:在模式串中(下标从0开始),next[i] 表示模式串中以下标 i 处字符结尾的子串的最大相同前后缀的长度。在KMP算法中,该值一方面表示模式串中1~i位置子串中的最长相同前后缀长度,另一方面表示在该位置匹配失败时模式串回溯比较的下一个字符位置(最长前缀末座标的下一个字符
如何求next数组

对于模式串 S 来说,首先初始化 next[0] = 0(一个字符不存在相同前后缀,所以长度为0)。假设在求取模式串 next 数组的过程中(与主串无关),已知 next[j] 现在要求 next[j+1] 则有以下两种情况:

  • 若 S[ j+1 ] == S[ next[j] ] : 则 next[j+1] = next[j]+1;(next[j] 的值表示长度,但在下标为0开始的字符数组中就表示相等前缀末下标的下一位,因此不用+1即可)

  • 若 S[ j+1 ] != S[ next[j] ] : 则说明该结尾处的相同前后缀应该比 j 处的更短一些,也就是我们要找一个更短的前缀和 j+1 处的后缀进行相等匹配。但是由 j 处的匹配可知 A~F 段的前后缀是相同的,因此这就等价于我们要在前缀 A~F 段中即 0~next[j]-1 中寻找一个尽可能大的相同前后缀。而之前的前后缀我们都已经求出了,于是比较就变成了 k = next[next[j] - 1] & S[j+1] == S[k]

在这里插入图片描述

算法实现:

void getNext(char* s,int len)
{
    next[0] = 0;
    int k = 0; //k = next[0]
    int i = 1;
    while(i < len)
    {
        if(s[i] == s[k])
        {
            next[i++] = ++k; //next[j+1] = k+1;
        }
        else
        {
            if(k > 0)k = next[k-1]; //k = next[k-1]
            else
            {
                next[i++] = k; //next[j+1] = 0 回溯到头了,找不到相同前缀,则最大相同前后缀长度=0
            }
        }
    }
}

经典例题

//返回主串T中子串S第一次出现的位置下标,找不到则返回-1
int kmp(char *T, char* S)
{
    int len_T = strlen(T);
    int len_S = strlen(S);
    for(int i = 0,j = 0;i<len_T;i++)
    {
        while(j > 0 && T[i] != S[j])j = next[j-1];
        if(T[i] == S[j])j++;
        if(j == len_S)return i-len_S+1;
    }
    return -1;
}

相关文章:

  • 特殊的数字排序
  • 【Agent】OpenManus-Agent-BaseAgent详细分析
  • PythonWeb开发框架—Flask-APScheduler超详细使用讲解
  • 软件架构设计习题及复习
  • HTML5 drag API实现列表拖拽排序
  • Solana
  • 新手村:混淆矩阵
  • 需求分析、定义、验证、变更、跟踪(高软47)
  • TypeScript
  • 【环境配置】windows下vscode下无法激活conda环境、创建虚拟环境报错
  • RAG的工作原理以及案例列举
  • 牛客周赛85 题解 Java ABCDEFG
  • 2025.3.10--2025.3.16学习周报
  • 大模型知识蒸馏核心技术(5)—— 多教师蒸馏
  • IT电子书的网站整理
  • esxi,vcenter6.0安装指导
  • 43运营干货:用户反馈收集及产品迭代流程
  • 在 React 中使用 Web Components 的实践操作
  • 【Django】【vue】设计一个评论模块
  • ospf单区域
  • 量子传感新技术“攻克”退相干难题
  • 解放日报社论:只争朝夕、不负重托,加快建成具有全球影响力的科技创新高地
  • 豆神教育:2024年净利润1.37亿元,同比增长334%
  • 中办、国办印发《安全生产考核巡查办法》
  • 王毅:坚持金砖团结合作,改革完善全球治理
  • 国家发改委答澎湃:将建立和实施育儿补贴制度,深入实施提振消费专项行动