题解 | 小红的01子序列计数(hard)

小红的01子序列计数(hard)

https://www.nowcoder.com/practice/4ec2369ff5e04bb88e0bf5473d26585c

#include <iostream>
#include <string>
using namespace std;

#define rep(i, a, b) for (int i = (a), _##i = (b); i <= _##i; ++i)

using ll = long long;

const int N = 1e5 + 5;
const int mod = 1e9 + 7;

// 全局变量
ll ans = 0;    // 最终答案
ll sum = 0;    // 当前窗口内0的数量,用于辅助更新
ll num0 = 0;   // 当前窗口内0的数量
ll sum0 = 0;   // 当前窗口内所有0的下标之和
ll num1 = 0;   // 当前窗口内1的数量
ll sum1 = 0;   // 当前窗口内"01"子序列数量

void solve() {
    int n;
    cin >> n;
    string s;
    cin >> s;

    // 为了模拟环形,把s复制一份自己接到自己后面,并在前面加空格使下标从1开始
    s = " " + s + s;

    // 初始化滑动窗口,处理前n个字符
    rep(i, 1, n) {
        if (s[i] == '0') {
            num0++;
            sum0 += i;
        } else { // s[i] == '1'
            num1++;
            sum1 += sum0;  // 新加入的1,与之前所有0形成"01"子序列
            sum += num0;   // 统计出现过的0的数量
        }
    }

    // 滑动窗口,处理后续n个字符
    rep(i, n + 1, 2 * n) {
        // 移除窗口左端点元素 i - n 的影响
        sum1 -= sum;      // 去掉以移除元素为起点产生的贡献
        sum0 -= num0;     // 更新所有0下标和
        if (s[i - n] == '0') {
            num0--;       // 0数量减少
            sum -= num1;  // 0被移除,减少对后续1的贡献
        } else {
            num1--;       // 1数量减少
            // 移除1不会影响sum
        }

        // 加入新的元素 s[i]
        if (s[i] == '0') {
            num0++;
            sum0 += n;    // 加入0,位置视为n(窗口固定大小)
        } else { // s[i] == '1'
            num1++;
            sum1 += sum0; // 新加入的1,与已有0形成新子序列
            sum += num0;  // 0的数量影响sum
        }

        // 累加当前窗口的贡献
        ans = (ans + sum1) % mod;
    }

    cout << ans << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    int t = 1;
    // cin >> t; // 如果有多组测试数据可以打开

    while (t--) {
        solve();
    }

    return 0;
}

#春招刷题训练营##春招笔试训练营#
全部评论

相关推荐

04-11 21:31
四川大学 Java
野猪不是猪🐗:(ja)va学弟这招太狠了
点赞 评论 收藏
分享
谁知道呢_:要掉小珍珠了,库库学三年,这个结果
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务