【题解】Atcoder ABC300 F.More Holidays(线性做法)

发布时间 2023-06-18 08:55:32作者: linyihdfj

F.More Holidays

题目描述:

给你一个由 ox 组成的长度为 \(N\) 的字符串 \(S\),以及整数 \(M\)\(K\)。保证 \(S\) 至少包含一个 x

假设 \(T\) 是由 \(S\) 复制 \(M\) 次而成的长度为 \(NM\) 的字符串。考虑将 \(T\) 中的 \(K\)x 恰好替换为 o

你的目标是在替换后的 \(T\) 中有尽可能长的由 o 组成的连续子串。

找出在替换后由 o 组成的连续子串的最大长度。

题目分析:

提供一种线性做法。

仿佛见过一道类似的题,不确定是不是就是这道。

考虑我们最后得到的极长连续的 o 串,一定是形如:(\(S\) 的一段后缀) + (若干个完整的 \(S\)) + (\(S\) 的一段前缀)

对于若干个完整的 \(S\) 我们可以直接求出来它的个数,设 \(tot\)\(S\)x 的个数则中间完整 \(S\) 的个数就是 \(\lfloor \frac{k}{tot} \rfloor\)

而对于 \(S\) 的一段后缀 + \(S\) 的一段前缀,我们可以考虑把他们拼在一起,也就是将 \(S\) 变成 \(SS\) 即二倍字符串长度,这样我们发现 \(SS\) 的任意一个子串都可以表示为上述形式,但是任意的上述形式就是 \(SS\) 的某一子串嘛?

其实不一定吧,因为我们上述的形式其实并不完整,因为我们可能连一整段都取不到所以可能最后取到的极长字串就是 \(S\) 内的一段区间,但是这样会发现虽然不完全,但是我们依旧可以对于 \(S\) 的每一个子串找到 \(SS\) 中的一个子串使得他们相等,所以就没有什么问题。

解决这个问题,一个想法就是直接枚举左端点,然后右端点就可以二分取得。

但是你会发现二分右端点是一个很傻的行为,因为随着左端点的增加右端点单调不降,所以可以直接使用双指针优化到 \(O(n)\)

需要注意的细节就是:有 \(m\) 个的限制所以可能我们没办法取得 (\(S\) 的一段后缀) + (\(S\) 的一段前缀)。

代码:

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e6+5;
char s[N];
int pre[N];
signed main(){
	int n,m,k;scanf("%lld%lld%lld",&n,&m,&k);
	scanf("%s",s+1);
	for(int i=1; i<=n; i++)	s[i+n] = s[i];
	for(int i=1; i<=2 * n; i++)	pre[i] = pre[i-1] + (s[i] == 'x');
	int ans = k / pre[n] * n,tmp = 0;
	m -= k / pre[n];
	k = k % pre[n];
	for(int l=1,r = 1; l<=n; l++){
		while(r + 1 <= 2 * n && pre[r + 1] <= k + pre[l-1]) ++r;
		if(m == 1)	tmp = max(tmp,min(r,n) - l + 1);
		else if(m > 1)	tmp = max(tmp,r - l + 1);
	}
	printf("%lld\n",ans + tmp);
	return 0;
}