Codeforces Round 984 (Div. 3)C题题解

Codeforces Round 984 (Div. 3) C题题解


C. Anya and 1100

请添加图片描述


题解

首先我们先了解什么是模拟

模拟就是用计算机来模拟题目中要求的操作,将题目要求转换为代码

解模拟题需要多打草稿理清逻辑思路,以这题为例:

题目要求在每次查询s[i-1]=v后判断字符串s中是否存在字串“1100”;

表面看是考察字符串的子串匹配,但如果我们使用strstr()或者find()来查找子串,在O(nq)的时间复杂度下,很容易TLE;

而且模式串“1100”较短也没有前后缀相等,更不可能用KMP或BM解决

于是我们考虑模拟q的查询操作:

由图

image

每次对pos位置的字符进行修改:

  • 如果修改前后的字符保持不变,即s[pos]==v,则不造成影响
  • 如果s[pos]!=v,则更改字符造成影响的区间为[pos-3,pos+3]
  • 所以我们只需要对[pos-3,pos+3]的区间进行子串的查找,大大减少查找次数

那么对该区间的查找结束了,如何判断整个字符串s是否存在子串“1100”呢?

我们可以在q次操作之前用朴素匹配算法查找s中出现子串"1100"的总次数,即:

bool is1100(ll i, string &p)
{
    if (i < 0)
        return false;
    if (i >= n - 3)
        return false;
    // 用于处理pos+3>n和pos-3<0的边界情况
    if (p[i] == '1' && p[i + 1] == '1' && p[i + 2] == '0' && p[i + 3] == '0')
    {
        return true;
    }
    return false;
}
int main()
{
    cin >> t;
    while (t--)
    {
        ll cnt = 0;//子串出现的次数
        cin >> s;
        cin >> q;
        n = s.length();
        for (ll i = 0; i < n; i++)
        {
            if (is1100(i, s))
            {
                cnt++;
            }
        }
	}
}

之后每次字符修改,cnt的值都可能变化:

  • 如果s[pos]!=v,cnt不变;
  • 如果s[pos]==v,则计算修改前后[pos-3,pos+3]区间子串“1100”的出现次数cnt加上两者之差

每次操作结束后,当cnt<=0时输出NO,否则输出YES;

       while (q--)
        {
            int l;
            char r;
            cin >> l >> r;
            l--;
            if (r != s[l])
            {
                bool origin = is1100(l - 3, s) || is1100(l - 2, s) || is1100(l - 1, s) || is1100(l, s);
                s[l] = r;
                bool current = is1100(l - 3, s) || is1100(l - 2, s) || is1100(l - 1, s) || is1100(l, s);
                cnt += current - origin;
            }
            string res = cnt ? "YES\n" : "NO\n";
            cout << res;
        }

完整代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

ll n = 0; // 字符串的长度
int t, q;
string s;

bool is1100(ll i, string &p)
{
    if (i < 0)
        return false;
    if (i >= n - 3)
        return false;
    // 用于处理pos+3>n和pos-3<0的边界情况
    if (p[i] == '1' && p[i + 1] == '1' && p[i + 2] == '0' && p[i + 3] == '0')
    {
        return true;
    }
    return false;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    cin >> t;
    while (t--)
    {
        ll cnt = 0;
        cin >> s;
        cin >> q;
        n = s.length();
        for (ll i = 0; i < n; i++)
        {
            if (is1100(i, s))
            {
                cnt++;
            }
        }
        while (q--)
        {
            int l;
            char r;
            cin >> l >> r;
            l--;
            if (r != s[l])
            {
                bool origin = is1100(l - 3, s) || is1100(l - 2, s) || is1100(l - 1, s) || is1100(l, s);
                s[l] = r;
                bool current = is1100(l - 3, s) || is1100(l - 2, s) || is1100(l - 1, s) || is1100(l, s);
                cnt += current - origin;
            }
            string res = cnt ? "YES\n" : "NO\n";
            cout << res;
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值