描述
小苯有个长度为 nn 的字符串 SS,其中有一些字符是白色的,其余的都是黑色。
现在小苯需要对字符串进行恰好 kk 次染色,具体的染色操作:
∙∙ 选择一个没有选择过的 下标 1≤i≤n1≤i≤n,接着将 SiSi 染成反色,即如果原本是白色,则染黑;原本是黑色,则染白。
染完色后,小苯会找在 SS 中找一个尽可能长的纯色子串 ,他想知道这个纯色子串的最大长度是多少,请你帮他算一算吧。
输入描述:
每个测试文件均包含多组测试数据。第一行输入一个整数 T(1≤T≤1000)T(1≤T≤1000) 代表数据组数,每组测试数据描述如下:
第一行两个正整数 n,k (1≤n≤106,0≤k≤n)n,k (1≤n≤106,0≤k≤n),分别表示 SS 的长度,以及小苯要染色的次数。
第二行一个长度为 nn 的 0101 串表示 SS。(si=1si=1 则表示白色,si=0si=0 表示黑色。)
(保证所有测试数据中,nn 的总和不超过 106106。)
输出描述:
对于每组测试数据,在单独的一行输出一个整数,表示染色完后 SS 中最长连续纯色子串的长度。
示例1
输入:
cpp
3
5 4
01010
5 1
01010
5 5
01010
输出:
cpp
3
3
1
说明:
对于第三组测试数据,注意由于要染色的位置必须不同,因此必须将所有字符都染一次,则 SS 变为:1010110101。
其中最长连续纯色子串长度是 11。
备注:
纯色子串:即 SS 中一段连续的子串,满足这个子串中所有字符颜色都相同。
法一:二分答案
设最终染色完后S中连续纯色子串的长度为L,子串L中原来不等于目标颜色的字符需要被染色,记这些字符数量为m, 则有:
由于需要恰好染色k次,故还有k-m次需要在子串L外完成,则有:
即:
综上,该子串L有如下性质:
满足上式的子串即一个合法子串,且该性质具有单调性。最大化L即为所求答案。
二分枚举L(O(n)),每次遍S中所有长L的子串,检查每个子串是否满足这一性质。显然,检查长L的子串是否满足该性质需要在O(1)完成,可以使用前缀和统计子串(区间)中0/1的数量。
总时间复杂度O(NlogN)。
cpp
#include<bits/stdc++.h>
using namespace std;
void solve(){
int n,k; cin>>n>>k;
string s; cin>>s;
s=' '+s;
vector<int>pre(n+10);
for(int i=1;i<=n;i++)
pre[i]=pre[i-1]+(s[i]=='1');
auto check = [&](int L){
int low=max(0, k+L-n);
int high=min(L, k);
if(low>high) return false;
for(int i=1;i+L-1<=n;i++){
int cnt1=pre[i+L-1]-pre[i-1];
int cnt0=L-cnt1;
if(cnt1>=low and cnt1<=high) return true;
if(cnt0>=low and cnt0<=high) return true;
}
return false;
};
int l=1,r=n;
while(l<r){
int mid = l+r+1>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
cout<<l<<endl;
}
int main(){
int t; cin>>t;
while(t--){
solve();
}
}
法二:滑动窗口
对于一个染色后长L的纯色子串,由于每个字符仅能被染色一次,l所以染色前L中非目标颜色的字符需要染色而等于目标颜色的字符不需要也不能被染色(染了L就不是纯色子串了)。
不妨设中需要染色的字符数量为yes,不需要染色的字符数量为no,再将L的左右边界的索引分别记作left,right,我们固定right,思考:left可以最远可以扩展至哪?
显然,扩展左边界left需要满足:
然后我们发现,对于A、B两子串,固定right(A),right(B),且right(A)<right(B),若A的左边最大扩展至left(A),则一定有:
由此,就可以使用滑动窗口(双指针)的思路,在知道left(A)的情况下快速求解left(B)了。
cpp
#include<bits/stdc++.h>
using namespace std;
void solve(){
int n,k; cin>>n>>k;
string s; cin>>s;
s=' '+s;
string all="01";
int ans=0;
for(auto ch:all){
int l=1,yes=0,no=0;
for(int r=1;r<=n;r++){
if(s[r]==ch) yes++;
else no++;
while(l<=r and (yes>n-k or no>k)){
if(s[l]==ch) yes--;
else no--;
l++;
}
ans=max(ans, r-l+1);
}
}
cout<<ans<<endl;
}
int main(){
int t; cin>>t;
while(t--){
solve();
}
}
26/1/25