【题解】Atcoder ABC300 F.More Holidays(线性做法)
F.More Holidays
题目描述:
给你一个由 o
和 x
组成的长度为 \(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;
}