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;
}

浙公网安备 33010602011771号