Luogu P3375 【模板】KMP字符串匹配

【模板】KMP字符串匹配

题目描述

给出两个字符串 \(s_1\)\(s_2\),若 \(s_1\) 的区间 \([l, r]\) 子串与 \(s_2\) 完全相同,则称 \(s_2\)\(s_1\) 中出现了,其出现位置为 \(l\)
现在请你求出 \(s_2\)\(s_1\) 中所有出现的位置。

定义一个字符串 \(s\) 的 border 为 \(s\) 的一个\(s\) 本身的子串 \(t\),满足 \(t\) 既是 \(s\) 的前缀,又是 \(s\) 的后缀。
对于 \(s_2\),你还需要求出对于其每个前缀 \(s'\) 的最长 border \(t'\) 的长度。

输入格式

第一行为一个字符串,即为 \(s_1\)
第二行为一个字符串,即为 \(s_2\)

输出格式

首先输出若干行,每行一个整数,按从小到大的顺序输出 \(s_2\)\(s_1\) 中出现的位置。
最后一行输出 \(|s_2|\) 个整数,第 \(i\) 个整数表示 \(s_2\) 的长度为 \(i\) 的前缀的最长 border 长度。

样例 #1

样例输入 #1

ABABABC
ABA

样例输出 #1

1
3
0 0 1

提示

样例 1 解释

对于 \(s_2\) 长度为 \(3\) 的前缀 ABA,字符串 A 既是其后缀也是其前缀,且是最长的,因此最长 border 长度为 \(1\)

数据规模与约定

本题采用多测试点捆绑测试,共有 3 个子任务

  • Subtask 1(30 points):\(|s_1| \leq 15\)\(|s_2| \leq 5\)
  • Subtask 2(40 points):\(|s_1| \leq 10^4\)\(|s_2| \leq 10^2\)
  • Subtask 3(30 points):无特殊约定。

对于全部的测试点,保证 \(1 \leq |s_1|,|s_2| \leq 10^6\)\(s_1, s_2\) 中均只含大写英文字母。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>

using namespace std;

char a[1000005], b[1000005]; 
int l1, l2, nex[1000005];

int main() {
	scanf("%s", a + 1);
	scanf("%s", b + 1);
	l1 = strlen(a + 1);
	l2 = strlen(b + 1);
	nex[0] = 1;
	for (int i = 2, j = 0; i <= l2; ++ i) {
		while (j > 0 && b[j + 1] != b[i]) {
			//如果失配,那么就不断向回跳
			//直到跳完后的子串后的下一个字符与b[i]相等或j跳到0 
			j = nex[j];
		}
		//通过自己匹配自己来得出kmp值 
		if (b[j + 1] == b[i]) j ++;
		nex[i] = j;
	}//自己与自己匹配找KMP 
	for (int i = 1, j = 0;i <= l1; ++ i) {
		while (j > 0 && b[j + 1] != a[i]) {
			j = nex[j];
		}
		if (b[j + 1] == a[i]) j ++;
		if (j == l2) {
			cout << i - l2 + 1 << endl;
			j = nex[j];
			//注意要跳到nex[j] 
		}
	}
	for (int i = 1; i <= l2; ++ i) {
		cout << nex[i] << " ";
	}
	return 0;
} 
posted @ 2023-06-11 14:38  觉清风  阅读(10)  评论(0编辑  收藏  举报