KMP 学习笔记与总结

发布时间 2023-07-11 12:07:48作者: Mingrui_Yang
KMP 学习笔记与总结

KMP

信息学奥赛一本通

img
img
img

模板

AcWing

// 下标从1开始
// s[]是长文本,p[]是模式串,n是s的长度,m是p的长度
求模式串的Next数组:
for (int i = 2, j = 0; i <= m; i ++ )
{
    while (j && p[i] != p[j + 1]) j = ne[j];
    if (p[i] == p[j + 1]) j ++ ;
    ne[i] = j;
}

// 匹配
for (int i = 1, j = 0; i <= n; i ++ )
{
    while (j && s[i] != p[j + 1]) j = ne[j];
    if (s[i] == p[j + 1]) j ++ ;
    if (j == m)
    {
        j = ne[j]; // j = 0;
        // 匹配成功后的逻辑
    }
}

注意:16行的 j = ne[j]可重叠的情况。
如果换成 j = ne[j]不可重叠的情况。

自己的

// 下标从1开始
void init() {
	for (int i = 1, j = 0; i <= n; i ++ ) {
		while (j && p[i + 1] != p[j + 1]) j = ne[j];
		if (p[i + 1] == p[j + 1]) j ++ ;
		ne[i + 1] = j;
	}
}
void Solve() {
	for (int i = 0, j = 0; i <= m; i ++ ) {
		while (j && s[i + 1] != p[j + 1]) j = ne[j];
		if (s[i + 1] == p[j + 1]) j ++ ;
		if (j == n) {
			cout << i + 1 - n << " "; // 匹配成功后的逻辑
			j = ne[j]; // j = 0;
		}
	}
}

注意:14行的 j = ne[j]可重叠的情况。
如果换成 j = ne[j]不可重叠的情况。

题目

题目1

模板题 - 可重叠 - luogu P3375 【模板】KMP 字符串匹配   

点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int n, m; // n主串长度 
char s1[N], s2[N]; // s1主串 
int ne[N];
int main() {
	cin >> s1 + 1 >> s2 + 1;
	n = strlen(s1 + 1), m = strlen(s2 + 1);
	for (int i = 1, j = 0; i <= m; i ++ ) {
		while (j && s2[i + 1] != s2[j + 1]) j = ne[j];
		if (s2[i + 1] == s2[j + 1]) j ++ ;
		ne[i + 1] = j;
	} 
	for (int i = 0, j = 0; i <= n; i ++ ) {
		while (j && s1[i + 1] != s2[j + 1]) j = ne[j];
		if (s1[i + 1] == s2[j + 1]) j ++ ;
		if (j == m) {
			cout << i + 1 - m + 1 << endl;
			j = ne[j];
		}
	}
	for (int i = 1; i <= m; i ++ )
		cout << ne[i] << " ";
	return 0;
}

题目2

其他题目 - 不可重叠 - LibreOJ #10043. 「一本通 2.2 例 1」剪花布条

题目可以转化为给定字符串 s1s2,求申 s1 中可以分割出多少个互不重叠的串 s2
直接用 KMP 算法即可,注意计算不重叠的匹配个数。

点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
int n, m;
char s1[N], s2[N]; // s1 是主串 
int ne[N], ans;
int main() {
	while (true) {
		cin >> s1 + 1;
		n = strlen(s1 + 1);
		if (s1[1] == '#' && n == 1) break;
		cin >> s2 + 1;
		m = strlen(s2 + 1); 
		// 初始化 ne 数组 
		for (int i = 1, j = 0; i <= m; i ++ ) {
			while (j && s2[i + 1] != s2[j + 1]) j = ne[j];
			if (s2[i + 1] == s2[j + 1]) j ++ ;
			ne[i + 1] = j;
		}
		// 匹配过程 
		ans = 0;
		for (int i = 0, j = 0; i <= n; i ++ ) {
			while (j && s1[i + 1] != s2[j + 1]) j = ne[j];
			if (s1[i + 1] == s2[j + 1]) j ++ ;
			if (j == m) {
				ans ++ ;
				j = 0; // 从头开始重新匹配,保证不重叠 
			}
		}
		cout << ans << endl; 
	} 
	return 0;
}