【学习笔记】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;
}
posted @ 2025-10-29 14:47  GuoSN0410  阅读(7)  评论(0)    收藏  举报