洛谷 P3375 【模板】KMP

题目描述

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

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

输入格式

第一行为一个字符串,即为 s1​。
第二行为一个字符串,即为 s2​。

输出格式

首先输出若干行,每行一个整数,按从小到大的顺序输出 s2​ 在 s1​ 中出现的位置。
最后一行输出∣s2​∣ 个整数,第 i 个整数表示 s2​ 的长度为 i 的前缀的最长 border 长度。

输入输出样例

输入 #1

ABABABC
ABA

输出 #1

1
3
0 0 1 

说明/提示

样例 1 解释

洛谷 P3375 【模板】KMP_第1张图片

对于 s2​ 长度为 3 的前缀 ABA,字符串 A 既是其后缀也是其前缀,且是最长的,因此最长 border 长度为 11。

数据规模与约定

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

  • Subtask 1(30 points):∣s1​∣≤15,∣s2​∣≤5。
  • Subtask 2(40 points):∣s1​∣≤10^4,∣s2​∣≤10^2。
  • Subtask 3(30 points):无特殊约定。

对于全部的测试点,保证 1≤∣s1​∣,∣s2​∣≤10^6,s1​,s2​ 中均只含大写英文字母。

 解题思路

本题为KMP算法模板题,数据大小为1e6,用朴素算法显然超时,然而KMP算法模板中字符串首字母下标为1,然而一般情况下首字母下标为0,这里只需要在求next数组中和匹配时都往前判断一个就行,求next数组时j==0改为j==-1,具体操作看代码。

AC代码

#include
#include
char a[1000010], b[1000010];
int next[1000010], i, j, k, h;
int main()
{
	scanf("%s %s", a, b);
	k = strlen(a);//主串长度
	h = strlen(b);//字串长度

	//预处理,求next数组
	next[0] = -1; i = 0; j = -1;
	while (i < h)
	{
		if (j == -1 || b[i] == b[j])
		{
			i++;
			j++;
			next[i] = j;
		}
		else
			j = next[j];
	}

	//子串与主串匹配
	i = 0; j = 0;//i为主串指针,j为子串指针
	while (i < k)//匹配条件 小于主串长度
	{
		if (a[i] == b[j]||j==-1)
		{
			i++;
			j++;
			if (j == h)//如果匹配成功
			{
				printf("%d\n", i - h + 1);//打印
				j = next[j];//回溯 继续判断
			}
		}
		else
		{
			j = next[j];//不匹配  回溯
		}
	}
	for (i = 1; i <= h; i++)//打印border,注意打印的border是从next数组第二个值开始的
		printf("%d ", next[i]);
	return 0;
}

你可能感兴趣的:(题组,算法)