Educational Codeforces Round 128 (Rated for Div. 2) C - Binary String(双指针 前缀和 二分答案求最大值最小问题)

15 篇文章 1 订阅
2 篇文章 0 订阅

题目来源

C Binary String

点此进入题面

题意:

你有一个 字符串 s ‎由字符 ‎0‎‎ 和 ‎‎1‎‎ 组成。‎

‎你必须从 字符串的开头 删除几个(可能是 0)字符,然后从 字符串的末尾 删除几个(可能是 0)字符。

‎‎删除后,字符串可能会变为空‎‎。删除的成本以下两个值 的 ‎‎最大值‎‎:‎

  • ‎字符串中 剩余的字符 ‎‎0‎‎ 的个数;‎
  • 从字符串中 删除的字符 ‎‎1‎ 的个数。‎

要求:算出可以达到‎‎的 最低‎‎移除成本 是多少?‎

思路:

一般地,对于此类求 “最大值最小” 的问题,我们可以考虑用 二分答案 的方法来做。

关于 二分答案 的学习,可以看看我之前设置的专栏:二分

二分答案 ansans 表示的是 最低移除成本,显然这是 一定有解 的,且这个 ans 一定是唯一的)。

之后,对于 每个枚举值 ans,我们使用使用 双指针O(n) 的时间复杂度 遍历一遍字符串,

双指针遍历要求

  • 双指针的区间内的 0 的数目 不能超过 ans(即 不可以超过最大值,这是显然的,因为题目要求的是 “最大值最小化”)。
  • 区间长度越长越好

之后判断 双指针区间之外 的部分的 1 的数目 是否也满足 不超过 ans,如果 的话,二分答案 中的 判定函数 check 就返回 true否则 返回 false

值得一提的是:(这其中的思想比较重要,比赛中比较常见,分别关于 二分双指针

  • 在双指针扫描的过程当中,只要 存在 双指针所维护区间之外的部分1 的数目 不超过 ans 就行, check 函数 直接返回 true
  • 双指针遍历 的时候,我们还可以站在 另一个角度 理解这个做法:用 一个指针 i 每次定好 中间部分区间右端点另一个指针 j 贪心选择移动 左端点

代码:

#define _CRT_SECURE_NO_WARNINGS 1
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#include <bits/stdc++.h>

using namespace std;

//#define int long long
typedef long long ll;
typedef unsigned long long ull;
typedef vector<int> vi;
typedef pair<int, int> pii;
typedef map<int, int> mi;
typedef vector<ll> vll;
typedef pair<ll, ll> pll;
#define pb push_back
#define pp pop_back
#define x first
#define y second
const int N = 2e5 + 10;
int len;
int s0[N], s1[N];

inline void Clear()
{
    for (int i = 1; i <= len; ++i) s0[i] = s1[i] = 0;
}

bool check(int ans)
{
    for (int i = 1, j = 0; i <= len; ++i)
    {
        while (j < i && s0[i] - s0[j] > ans) ++j;
        if (s1[j] + s1[len] - s1[i] <= ans) return true;
    }
    return false;
}

signed main()
{
    int T = 1; cin >> T;

    while (T--)
    {
        Clear();

        string s; cin >> s;
        len = s.size();
        s = " " + s;

        for (int i = 1; i <= len; ++i)
        {
            s0[i] = s0[i - 1] + ((s[i] - '0') ^ 1);
            s1[i] = s1[i - 1] + (s[i] - '0');
        }

        int l = 0, r = len;

        while (l < r)
        {
            int mid = l + r >> 1;
            if (check(mid)) r = mid;
            else l = mid + 1;
        }

        cout << l << '\n';
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值