What is KMP

107 阅读7分钟

What is KMP?

很多可爱的西柚会发出这样的疑问?这啥算法啊?没见过啊?

废话不说,让我们从什么是kmp,kmp有什么用处,kmp适用于哪里和kmp怎么用来逐个剖析。

什么是KMP?

资料:七分钟理解什么是 KMP 算法 - 知乎

KMP:(Knuth-Morris-Pratt) 字符串查找算法 *1,简称KMP算法。常用于在一个文本串 S 内查找一个模式串 P 的出现位置。

下面直接给出 KMP算法 的操作流程:

  • 假设现在文本串 S 匹配到 i 位置,模式串 P 匹配到 j 位置
  • 如果 j = -1,或者当前字符匹配成功(即 S[i] == P[j] ),都令 i++,j++,继续匹配下一个字符; 如果 j != -1,且当前字符匹配失败(即 S[i] != P[j] ),则令 i 不变,j = next[j]。此举意味着失配时,模式串 P相对于文本串 S 向右移动了 j - next [j] 位
  • 换言之,将 *模式串 2 P 失配位置的 next 数组的值对应的 模式串 P 的索引位置移动到失配处

kmp有什么用处? 适用于哪些地方呢?

它是匹配字符串子串的一个方法(算法),同时它提供相较于暴力算法BF更少的比较次数和交换次数!

多适用于寻找字串的题目,方便提供是否找到,地址,以及他的计算次数之类的数据.

kmp的使用规则--(底层逻辑)

首先,列出模式串 P 的所有子串:

img

然后,求得每一个子串的所有前缀与后缀。

前缀 指除了最后一个字符以外,一个字符串的全部头部组合;后缀 指除了第一个字符以外,一个字符串的全部尾部组合。

以第五列为例进行演示。

前缀

img

后缀

img

因此,它的前缀后缀的公共元素的最大长度为 2

求得原模式串 P 的子串对应的各个前缀后缀的公共元素的 最大长度表 下图。

img

根据最大长度表 去求 next 数组next 数组相当于“最大长度值” 整体向右移动一位,然后初始值赋为-1

img

好了,获取了 next 数组 后,KMP 算法 的操作就很清晰了。

将模式串 P 与文本串 S 的字母一个个进行匹配,当失配的时候,模式串向右移动。

怎么移动?

img

比如模式串的 b 与文本串的 c 失配了,找出失配处模式串的 next数组 里面对应的值,这里为 0,然后将索引为 0 的位置移动到失配处。

img

KMP的代码实现:

在理解了前缀表及其作用之后,KMP算法就可以整体上分为两步:

一、计算前缀表。

二、根据前缀表移动两个指针进行匹配。

下面我们给出KMP算法的代码实现如下:

void get_Next(string s, int next[])     //这个函数对字符串s进行预处理得到next数组
{
    int j = 0;
    next[0] = 0;    //初始化
    for(int i = 1; i<s.size(); i++){    //i指针指向的是后缀末尾,j指针指向的是前缀末尾
        while(j>0&&s[i]!=s[j])  j = next[j-1];  //前后缀不相同,去找j前一位的最长相等前后缀
        if(s[i]==s[j])  j++;    //前后缀相同,j指针后移
        next[i] = j;    //更新next数组
    }
}
​

上面是计算模式串的前缀表next的代码,其流程见注释。

得到了next数组后,我们进入匹配查找的阶段。

int kmp_strstr(string s, string t)  //这个函数是从s中找到t,如果存在返回t出现的位置,如果不存在返回-1
{
    if(t.size()==0) return 0; //或t.length() == 0 两者等价
    get_Next(t, next);
    int j = 0;
    for(int i = 0; i < s.size(); i++){
        while(j>0&&s[i]!= t[j]) j = next[j-1];  
        if(s[i]==t[j])  j++;
        if(j==t.size()) return i - t.size() + 1;
    }
    return -1;
}
​

总结

KMP算法主要应用于字符串模式匹配,其核心思想在于使用前缀表从而实现在不匹配时利用之前已经匹配过的信息来减少回溯的过程。理解KMP算法的关键在于理解前缀表的生成,这部分可以手推几次应该就能理解。


字符串查找算法是指在文本串中搜索特定模式或子串位置的算法。具体来说:

  • 定义和用途

    • 字符串查找算法用于在一个较大的文本串(如S)中找到一个较小的模式串(如P)出现的位置。
    • 这些算法通常需要遍历文本串中的每一个字符,并尝试与模式串进行匹配。
  • 常见算法

    • 朴素算法:直接比较文本串和模式串的每一位,直到找到完全匹配的部分或者遍历完整个文本串。
    • KMP算法:通过预处理模式串生成部分匹配表(next数组),在不匹配时利用该表跳过一些字符,从而提高效率。
    • Boyer-Moore算法:从模式串的末尾开始比较,并根据坏字符规则和好后缀规则决定如何移动模式串,以减少不必要的字符比较。
    • Sunday算法:类似于Boyer-Moore算法,但仅使用坏字符规则来决定模式串的移动方式,简单且高效。
  • 数据结构支持

    • 字符串查找算法依赖于有效的数据结构来存储和组织字符序列,这些数据结构可以是简单的数组或者是更复杂的结构,如前缀表、坏字符表等。
    • 数据结构的选择直接影响到算法的执行效率和复杂度。
  • 数学原理和知识

    • 大多数高效的字符串查找算法都涉及一定的数学原理,例如KMP算法中的next数组计算就是基于模式串的前缀和后缀信息。
    • 这些数学原理使得算法能够快速地定位到可能的匹配点,而不需要对每个字符都进行完整的比较。

总的来说,字符串查找算法是一类旨在提高文本串中模式串搜索效率的方法,它们通过不同的策略减少了不必要的字符比较,提高了查找速度。

模式串是指在字符串匹配算法中用于与主串进行比较的子序列。 具体来说,模式串是在文本中需要查找的目标字符串。例如,在搜索文档时,输入的查询词就是模式串,而整个文档内容则是主串。

  • 模式串的具体含义

    • 模式串是被用来在更长的文本(主串)中寻找的位置或子序列。
    • 它可以是一个单词、短语或一系列字符,目的是确定这些字符是否出现在主串中的某个位置。
  • 模式串的应用场景

    • 在文本编辑器中查找特定单词或短语。
    • 在搜索引擎中,用户输入的查询词被视为模式串,系统会尝试在大量文档中找到包含该模式串的内容。
    • 网页爬虫使用模式串来识别网页中的特定信息。
  • 模式串与主串的关系

    • 主串是待搜索的大文本,模式串是在其中进行搜索的小文本。
    • 通过比较模式串和主串的各个部分,可以确定模式串是否存在于主串中以及其出现的位置。
  • 模式串在不同算法中的应用

    • BF算法(暴力算法) :简单地将模式串逐个字符与主串进行比较,直到找到匹配或遍历完主串。
    • KMP算法:利用模式串的部分匹配信息(如next数组),提高匹配效率,减少不必要的字符比较次数。
    • AC自动机:不仅支持模式串的精确匹配,还能处理多个模式串的同时匹配,并且能够处理正则表达式的模式串。