双指针 滑动窗口 经典问题 : 选择一个尽可能长的区间,使得区间中恰好包含 k 个 0,把这些 0 染成 1 得到最长1串

https://ac.nowcoder.com/acm/contest/108576/E
给定一个长度为 n 的 01 串,其中 1 表示白色,0 表示黑色。现在需要对这个字符串进行恰好 k 次染色操作,每次染色操作必须选择之前未被选择过的位置,将该位置的颜色取反(即白色变黑,黑色变白)。染色完成后,求最长连续纯色子串的长度(即连续的一段字符,所有字符颜色相同)。
解题思路
我们注意到最终的最长纯色子串要么全是 0,要么全是 1。因此我们可以分别考虑两种情况:
以全 1 为目标的最长子串长度。
以全 0 为目标的最长子串长度。
我们可以通过翻转字符串(0 变 1,1 变 0)来将求全 0 子串的问题转化为求全 1 子串的问题。
核心思路:
我们只需实现一个函数 work(s),计算在翻转恰好 k 次后,字符串 s 中最长的全 1 子串长度。然后分别对原字符串和翻转后的字符串调用 work,取最大值即可。
实现 work(s) 函数:
假设当前字符串为 s,我们要计算翻转恰好 k 次后,最长的全 1 子串长度。
统计字符数量:
设字符串中 0 的数量为 X,1 的数量为 Y。
分情况讨论:
情况1:当 k >= X 时:
我们可以将所有 0 翻转为 1,此时还剩下 k - X 次翻转。
此时整个字符串已经全是 1,我们需要用剩下的 k - X 次翻转,将一些 1 翻转回 0。
为了使得最长全 1 子串尽可能长,我们应当尽量少地翻转 1 为 0。
因此,我们最多允许翻转 k - X 个 1 为 0,即最长全 1 子串最多包含 Y - (k - X) 个原始 1。
问题转化为:在字符串中找到最长的连续子串,其原始 1 的数量恰好为 Y - (k - X)。
使用滑动窗口(双指针)解决。
情况2:当 k < X 时:
我们无法将所有 0 翻转为 1,只能翻转其中的 k 个 0 为 1。
为了使最长全 1 子串尽可能长,我们应当选择一段连续的子串,其中恰好包含 k 个 0(翻转后全部变成 1),其余字符均为 1。
问题转化为:在字符串中找到最长的连续子串,其原始 0 的数量恰好为 k。
使用滑动窗口(双指针)解决。

Solution Code:

void solve() {
    int n, K;
    string s;
    cin >> n >> K >> s;
     
    auto work = [&](string s) -> int {
        s = " " + s;
        int X = 0, Y = 0, k = K;
        for(int i = 1; i <= n; i++) {
            X += (s[i] == '0');
            Y += (s[i] == '1');
        }
 
        int ans = 0;
        if(k > X) {
            k -= X;
            Y -= k;
            // cerr << "Y : " << Y << ", k : " << k << endl;
            int cnt = 0;
            for(int i = 1, j = 1; i <= n; i++) {
                cnt += (s[i] == '1');
                while(cnt > Y && j <= i) {
                    if(s[j] == '1') {
                        cnt -- ;
                    }
                    j ++ ;
                }
                if(cnt == Y && j <= i) {
                    ans = max(ans, i - j + 1);
                }
            }
            return ans;
        }
 
        X = 0;
        for(int i = 1, j = 1; i <= n; i++) {
            X += (s[i] == '0');
            while(X > k && j <= i) {
                if(s[j] == '0') {
                    X -- ;
                }
                j ++ ;
            }
            if(j <= i && X == k) {
                ans = max(ans, i - j + 1);
            }
        }
        return ans;
    };
 
    int ans = work(s);
    for(int i = 0; i < n; i++) { // flip the string
        s[i] ^= 1;
    }
    ans = max(ans, work(s));
 
    cout << ans << endl;
}
posted on 2025-08-08 14:43  下头小美  阅读(8)  评论(0)    收藏  举报