KMP (学习笔记)(25.11.14)

KMP (学习笔记)

概述

一种可以在A串中查找B串的数量与位置的算法,可以使时间复杂度降低到\(O(n+m)\),使用的理念就是去替换相同位置防止去做太多无意义判断

推导

kmp的优化点在于,每次失配(匹配失败)后,不会从头开始枚举子串,而是从一个特定的位置开始匹配(对于每个位置所存的特定位置就是nxt数组)

这个特定位置是什么呢,我们由一组样例引入(使用kmp规则)

以下便表示第一次匹配失败了

abcabd
abcababcd

所以我们后移其位置

   abcabd
abcababcd

发现了么,是将其后移到后面第一个与子串相重合的位置,所以我们也将其称为:失配元素的前一个元素所对应的失配数组

这个数组有什么特征呢,每个元素存的是子串的最大前缀后缀相同长度,所以对于一个位置的元素,我们可以将一个前缀替换到这个后缀(因为都一样,所以没有再枚举的必要),继续做比较,节省很多时间

实现

关于为什么要用\(j+1\),是因为我们在每回判断后去回退的数组是失配元素的前一个,所以用\(j+1\)就可以让失配元素直接使用,方便点

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6+10;
string s1, s2;
int nxt[N];
int main() {
    cin>>s1>>s2;
    int l1 = s1.size(), l2 = s2.size();
    s1 = ' '+s1;s2 = ' '+s2;
    int j=0;
    //自己匹配自己
    for (int i=2; i<=l2; i++) {
        //如果之前的没法与现在的相等,就用失配数组跳到更前面
        while (j&&s2[i]!=s2[j+1])
            j=nxt[j];
        //如果两个相等了,就加一下
        if (s2[j+1]==s2[i])j++;
        nxt[i] = j;
    }
    j=0;
    for (int i=1; i<=l1; i++) {
        //只要不等于0,就一直做枚举,然后去往前找
        while (j>0&&s2[j+1]!=s1[i])
            j = nxt[j];
        //如果两个一样了,就可以去加一下
        if (s2[j+1]==s1[i])j++;
        if (j==l2) {
            cout<<i-l2+1<<'\n';
            j = nxt[j];
        }
    }
    for (int i=1; i<=l2; i++) {
        cout<<nxt[i]<<' ';
    }
    return 0;
}

posted @ 2025-11-14 15:14  Yuriha  阅读(16)  评论(0)    收藏  举报