洛谷 P5410 【模板】扩展KMP

本文介绍了洛谷P5410题目的解决方案,重点讲解了扩展KMP算法,又称Z Algorithm,其思路与Manacher算法相似。分析中提到,通过next数组计算模式串的公共前缀,利用这一性质逐步扩展并更新最远位置,最终实现O(n)的时间复杂度。文中还提供了AC代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接

https://www.luogu.org/problem/P5410

分析

国外一般称此算法为Z Algorithm,个人感觉算法思想上和Manacher算法更为接近;

n e x t [ i ] next[i] next[i] 表示模式串 S S S 后缀 i . . . n i ... n i...n S S S 的最大公共前缀长度;

若已求 n e x t [ 1... x − 1 ] next[1 ... x - 1] next[1...x1],接下来要求 n e x t [ x ] next[x] next[x]

记录当前扩展最远的点,即 m a x 1 ≤ k < x ( k + n e x t [ k ] − 1 ) max_{1 \leq k < x} (k + next[k] - 1) max1k<x(k+next[k]1)

n e x t next next 数组定义可知, S [ 1... n e x t [ k ] ] = S [ k . . . k + n e x t [ k ] − 1 ] S[1 ... next[k]] = S[k ... k + next[k] - 1] S[1...next[k]]=S[k...k+next[k]1]

S [ 1... n e x t [ x − k + 1 ] ] = S [ x − k + 1... x − k + n e x t [ x − k + 1 ] ] S[1 ... next[x - k + 1]] = S[x - k + 1 ... x - k + next[x - k + 1]] S[1...next[xk+1]]=S[xk+1...xk+next[xk+1]]

S [ x − k + 1... x − k + n e x t [ x − k + 1 ] ] = S [ x . . . x + n e x t [ x − k + 1 ] − 1 ] S[x - k + 1 ... x - k + next[x - k + 1]] = S[x ... x + next[x - k + 1] - 1] S[xk+1...xk+next[xk+1]]=S[x...x+next[xk+1]1]

故有 S [ 1... n e x t [ x − k + 1 ] ] = S [ x . . . x + n e x t [ x − k + 1 ] − 1 ] S[1 ... next[x - k + 1]] = S[x ... x + next[x - k + 1] - 1] S[1...next[xk+1]]=S[x...x+next[xk+1]1]

x + n e x t [ x − k + 1 ] − 1 x + next[x - k + 1] - 1 x+next[xk+1]1 未到达扩展到的最远位置,则 n e x t [ x ] next[x] next[x] 便可确定;

否则,暴力先后扩展,并更新扩展到的最远位置,

仿照KMP算法,利用 n e x t next next 数组求出原问题答案;

扩展到的最远位置要么增加,要么不变,复杂度为 O ( n ) O(n) O(n)

AC代码

#include <cstdio>
#include <cstring>

const int maxn = 1e5 + 5;

int nxt[maxn], ex[maxn], l1, l2;
char a[maxn], b[maxn];

inline void get_nxt() {
	nxt[1] = l2;
	for (int i = 1; i < l2 && b[i] == b[i + 1]; ++i) ++nxt[2];
	int k = 2;
	for (int i = 3; i <= l2; ++i) {
		int p = k + nxt[k] - 1;
		if (i + nxt[i - k + 1] - 1 < p) nxt[i] = nxt[i - k + 1];
		else {
			int j = p - i + 1;
			if (j < 1) j = 1;
			while (i + j - 1 <= l2 && b[i + j - 1] == b[j]) ++j;
			nxt[i] = j - 1, k = i;
		}
	}
}

inline void exkmp() {
	get_nxt();
	for (int i = 1; i <= l1 && i <= l2; ++i) {
		if (a[i] != b[i]) break;
		++ex[1];
	}
	int k = 1;
	for (int i = 2; i <= l1; ++i) {
		int p = k + ex[k] - 1;
		if (i + nxt[i - k + 1] - 1 < p) ex[i] = nxt[i - k + 1];
		else {
			int j = p - i + 1;
			if (j < 1) j = 1;
			while (i + j - 1 <= l1 && j <= l2 && a[i + j - 1] == b[j]) ++j;
			ex[i] = j - 1, k = i;
		}
	}
}

int main() {
	scanf("%s%s", a + 1, b + 1);
	l1 = strlen(a + 1), l2 = strlen(b + 1);
	exkmp();
	for (int i = 1; i <= l2; ++i) printf("%d ", nxt[i]);
	puts("");
	for (int i = 1; i <= l1; ++i) printf("%d ", ex[i]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值