题解 [LuoguP3426][POI2005]SZA-Template

Luogu Link

题解 P3426(kmp)

首先求出 next \text{next} next 数组(记为 n x t i nxt_i nxti)。

f i f_i fi 表示印制 [ 1 , i ] [1,i] [1,i] 所需最小印章, g i g_i gi 表示 [ 1 , i ] [1,i] [1,i] 的印章最长能印多长。

可以发现 f i f_i fi 只会有两种值: i , f n x t i i,f_{nxt_i} i,fnxti。证明如下。

  • 如果 f i < f n x t i f_i<f_{nxt_i} fi<fnxti,但 [ 1 , f i ] [1,f_i] [1,fi] 必然能印制完 [ 1 , n x t i ] [1,nxt_i] [1,nxti],则 f i ≥ f n x t i f_i \geq f_{nxt_i} fifnxti 矛盾。
  • 如果 f i > f n x t i f_i > f_{nxt_i} fi>fnxti f i ≠ i f_i \neq i fi=i,因为在同一位置上印不同字符是不允许的,所以必然 [ 1 , f i ] [1,f_i] [1,fi] [ 1 , i ] [1,i] [1,i] 的后缀,也是前缀,所以可以用 f i f_i fi 更新 n x t i nxt_i nxti 的值,矛盾。
  • 如果 f i > i f_i > i fi>i,显然不成立。

接下来考虑什么时候 f i f_i fi 能取到 f n x t i f_{nxt_i} fnxti:考虑 g n x t i g_{nxt_i} gnxti,若 g n x t i ≥ i − n x t i g_{nxt_i}\geq i-nxt_i gnxtiinxti,则 f i f_i fi 能取到 f n x t i f_{nxt_i} fnxti。在计算完 f i f_i fi 的时候更新 g f i g_{f_i} gfi i i i 即可。

//P3426
#include <cstring>
#include <cstdio>

const int N = 5e5 + 10;
int n, nxt[N], f[N], g[N];
char s[N];

int main(){
	scanf("%s", s+1);
	n = strlen(s+1);
	for(int i = 2, j = 0; i <= n; ++ i){
		while(j > 0 && s[i] != s[j+1]) j = nxt[j];
		if(s[i] == s[j+1]) ++ j;
		nxt[i] = j;
	}
	for(int i = 1; i <= n; ++ i){
		if(g[f[nxt[i]]] >= i-nxt[i]) f[i] = f[nxt[i]];
		else f[i] = i;
		g[f[i]] = i;
	}
	printf("%d\n", f[n]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值