算法学习笔记(16)——KMP

KMP

KMP 算法,又称模式匹配算法,能够在线性时间内判定字符串 P[1N] 是否为字符串 S[1M] 的子串。并求出字符串 P 在字符串 S 中各次出现的位置。

首先,一个 O(NM) 的朴素做法是,尝试枚举字符串 S 中的每个位置 i,把字符串 P 与字符串 S 的后缀 S[iM] 对齐,向后扫描逐一比较 P[1]S[i]P[2]S[i+1],...... 是否相等。我们把这种比较过程称为 PS 尝试进行“匹配”。

KMP算法能够更高效、更准确地处理这个问题,并且能够为我们提供一些额外的信息。详细地讲,KMP算法分为两步。

  1. 对字符串 P 进行自我“匹配”,求出一个数组 next,其中 next[i] 表示“P 中以 i 结尾的非前缀子串”与“P 的前缀”能够匹配的最长长度,即:

    next[i]=max{j},j<iP[ij+1i]=P[1j]

    特别的,当不存在这样的 j 时,令 next[j]=0
  2. 对字符串 PS 进行匹配,求出一个数组 f,其中 f[i] 表示“ S 中以 i 结尾的子串”与“P 的前缀”能够匹配的最大长度,即:

    f[i]=max{j},jiS[ij+1i]=P[1j]

下面介绍 next 数组的求法:

1. 初始化 next[1] = j = 0,假设 next[1 ~ i-1] 已求出,下面求解 next[i]
2. 不断尝试扩展匹配长度 j,如果扩展失败(下一个字符不相等),令 j 变为 next[j],直至 j 0(应该重新从头开始匹配)
3. 如果能够扩展成功,匹配长度 j 就增加 1。next[i] 的值就是 j。

求解 f 与求解 next 的过程基本一致,具体参考模板代码。

题目链接:AcWing 831. KMP字符串

#include <iostream>

using namespace std;

const int N = 1e5 + 10, M = 1e6 + 10;

int n, m;
char p[N];  // 模版串
char s[M];  // 模式串
int ne[N];  // next数组

int main()
{
    // 注意,此处从下标1开始读入字符串,方便后续操作
    cin >> n >> p + 1 >> m >> s + 1;
    
    // 1. 预处理next数组,默认next[1] = 0,从2开始处理
    for (int i = 2, j = 0; i <= n; i ++ ) {
        // 当j不是第一个匹配点且匹配不成功时,从下一候选项尝试
        while (j && p[i] != p[j + 1]) j = ne[j];
        // 匹配成功
        if (p[i] == p[j + 1]) j ++;
        ne[i] = j;
    }
    
    // 2. 匹配
    for (int i = 1, j = 0; i <= m; i ++ ) {
        while (j && s[i] != p[j + 1]) j = ne[j];
        if (s[i] == p[j + 1]) j ++;
        // 此时就是 P 在 S 中的某一次出现
        if (j == n) {
            // 输出匹配成功的起始位置
            cout << i - n << ' ';
            // 从下一候选项继续尝试匹配
            j = ne[j];
        }
    }
    
    return 0;
}

在上面代码的while循环中,j的值不断减小,j=ne[j]的执行次数不会超过每层for循环开始时j的值与while循环结束时j的值之差。而在每层for循环中,j的值至多增加 1。因为j始终非负,所以在整个计算过程中,j减小的幅度总和不会超过j增加的幅度总和。故j的总变化次数至多为 2(N+M)。整个算法的时间复杂度为 O(N+M)。这样只需遍历一次 S 就可以得到 P 的所有匹配位置。

  • 计算Partial_Table(或者说是计算模式串的最长公共前缀后缀长度列表)时的比较次数介于[m,2m],假设m是模式串的长度.
  • 比较模式串和子串时比较次数介于[n,2n],最坏情形形如T="aaaabaaaab",P="aaaaa".
posted @   S!no  阅读(37)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示