ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

牛客寒假算法基础集训营3 I 智乃的密码(二分、尺取)

2022-02-04 23:32:04  阅读:138  来源: 互联网

标签:pre cnt int ++ else 牛客 端点 尺取 集训营


题目链接

题目大意:

给定字符串 \(s\) 、\(L\) 、\(R\) ,求满足长度为 \([L, R]\) 且至少包含四类字符中的三种的子串数量。

思路:

当固定了区间左端点时,随着右端点向右移动对答案的贡献具有单调性。同样,固定右端点,向右移动左端点,对答案的贡献也有单调性。我们考虑使用尺取。

固定区间的左端点为 \(i\) 时,找可行的右端点的取值范围。

Code:
int main() {
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int n, L, R; cin >> n >> L >> R;
    string s; cin >> s;
    map<int, int> cnt; // 四类字符的数量
 
    auto upd = [&](char ch, int dx) {
        if (isupper(ch)) {
            cnt[0] += dx;
        } else if (islower(ch)) {
            cnt[1] += dx;
        } else if (isdigit(ch)) {
            cnt[2] += dx;
        } else {
            cnt[3] += dx;
        }
        return;
    };
 
    ll ans = 0;
    for (int i = 0, j = 0; i < n; i++) { // [, )
        while (j <= n && (cnt[0] > 0) + (cnt[1] > 0) + (cnt[2] > 0) + (cnt[3] > 0) < 3) {
            if (j < n)
                upd(s[j], 1);
            j++;
        }
        int l = max(j, i + L); // 此时j是刚好满足出现次数的位置,i + L表示符合条件的右端点至少距离长度为L
        int r = min(n, i + R); // 右端点不能取越界,且距离最多不能超过R
        ans += max(0, r - l + 1);
        upd(s[i], -1);
    }
    cout << ans << "\n";
    return 0;
}
 
/*
11 3 6
11111bAAA**
 
7 3 6
11111bA
 
 */

左端点已经固定,假设此时第一个满足条件的右端点下标为 \(idx\),那么根据单调性在这之后的下标也可以当作右端点。我们可以二分求出第一个满足条件的右端点。这里使用前缀和加速对区间内子串数量的查询。

int main() {
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int n, L, R; cin >> n >> L >> R;
    string s; cin >> s;
    vector<vector<int>> pre(4, vector<int>((int)s.length() + 1));
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < 4; j++)
            pre[j][i + 1] = pre[j][i];
        if (isupper(s[i]))
            pre[0][i + 1]++;
        else if (islower(s[i]))
            pre[1][i + 1]++;
        else if (isdigit(s[i]))
            pre[2][i + 1]++;
        else
            pre[3][i + 1]++;
    }

    auto check = [&](int l, int r) {
        int cnt = 0;
        for (int i = 0; i < 4; i++) {
            cnt += (pre[i][r] - pre[i][l - 1] > 0);
        }
        return cnt >= 3;
    };

    ll ans = 0;
    for (int i = 1; i <= n; i++) {
        int l = i, r = min(i + R, n + 1), idx = 0;
        while (l < r) {
            int mid = (l + r) >> 1; // 找第一个满足条件的右端点
            if (check(i, mid)) {
                idx = mid;
                r = mid;
            } else {
                l = mid + 1;
            }
        }
        if (idx == 0) // 一个满足条件的都没找到
            continue;
        l = max(idx, i + L - 1);
        r = min(n, i + R - 1);
        // cerr << "l = " << l << ", r = " << r << endl;
        ans += max(0, r - l + 1);
    }
    cout << ans << "\n";
    return 0;
}

标签:pre,cnt,int,++,else,牛客,端点,尺取,集训营
来源: https://www.cnblogs.com/Nepenthe8/p/15863640.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有