第六届传智杯程序设计国赛B组T4·小苯的字符串染色

描述

小苯有个长度为 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

相关推荐
Charlie_lll9 小时前
力扣解题-移动零
后端·算法·leetcode
chaser&upper9 小时前
矩阵革命:在 AtomGit 解码 CANN ops-nn 如何构建 AIGC 的“线性基石”
程序人生·算法
weixin_499771559 小时前
C++中的组合模式
开发语言·c++·算法
iAkuya9 小时前
(leetcode)力扣100 62N皇后问题 (普通回溯(使用set存储),位运算回溯)
算法·leetcode·职场和发展
近津薪荼9 小时前
dfs专题5——(二叉搜索树中第 K 小的元素)
c++·学习·算法·深度优先
xiaoye-duck9 小时前
吃透 C++ STL list:从基础使用到特性对比,解锁链表容器高效用法
c++·算法·stl
松☆9 小时前
CANN与大模型推理:在边缘端高效运行7B参数语言模型的实践指南
人工智能·算法·语言模型
java干货10 小时前
为什么 “File 10“ 排在 “File 2“ 前面?解决文件名排序的终极算法:自然排序
开发语言·python·算法
皮皮哎哟10 小时前
数据结构:嵌入式常用排序与查找算法精讲
数据结构·算法·排序算法·二分查找·快速排序
程序员清洒10 小时前
CANN模型剪枝:从敏感度感知到硬件稀疏加速的全链路压缩实战
算法·机器学习·剪枝