【学习笔记】kmp
简介
第一篇串串学习笔记。
kmp 是一种高效的模式串匹配算法,可以在 \(O(n)\) 时间复杂度内利用字符串的 border 高效维护模式串 t 在模式串 s 中的出现位置等信息。
实现
给出 border 的定义:最长的出现在后缀中的前缀(不能是串本身)。
首先有一个性质:串 s 的非最长 border 一定是最长 border 的 border。证明显然,画画图就理解了。 QAQ
考虑 kmp 的执行过程:
假设现在要匹配 s 的第 \(i\) 位和 t 串的第 \(j +1\) 位,若两个字符不同,显然不能将 \(j\) 往右移(若能向后移动,在前面匹配时早就移动了,\(j\) 会比此时的 \(j\) 更大),所以只能向左移,假设将 \(j\) 移到了 \(k\),画个图就发现 \(t_1\dots t_k\) 一定是 \(t_1\dots t_j\) 的一个 border,因此可以往前跳 border,直到 \(t_{j + 1} = s_i\) 或者 \(j = 0\)。
那么如何求一个串的 border:
类似上面的过程,若当前字符不匹配,显然要缩小匹配串的长度,往前跳 border,直到字符相匹配。
注意 算 border 时,初始 \(i =2 , j = 0\)(防止将自身作为自身的border 导致死循环 ),而匹配时则 \(i = 1, j = 0\)。
code
终于到了喜闻乐见的代码环节
#include<bits/stdc++.h>
#define N 1000010
#define fo(a, b, c) for(int b = a; b <= c; b++)
using namespace std;
int n, m, b[N];
string s, t;
int main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin >> s >> t;
n = s.size(), s = " " + s;
m = t.size(), t = " " + t;
for(int i = 2, j = 0; i <= m; i++){
while(j && t[i] != t[j + 1]) j = b[j];
if(t[j + 1] == t[i]) j++;
b[i] = j;
}
for(int i = 1, j = 0; i <= n; i++){
while(j && t[j + 1] != s[i]) j = b[j];
if(t[j + 1] == s[i]) j++;
if(j == m){
cout << i - j + 1 << "\n";
j = b[j];
}
}
fo(1, i, m) cout << b[i] << ' ';
return 0;
}

浙公网安备 33010602011771号