Avatar
“”

一个兴趣使然的追梦人

▼

AIGC标识 Codeforces Round 1017 (Div. 4)_D. Tung Tung Sahur

D. Tung Tung Sahur传送门《-点我

题解:D. Tung Tung Sahur

题目大意

有两种鼓:左鼓 \(\text{L}\) 和右鼓 \(\text{R}\)。敲击鼓点时,每次击打可能发出 \(1\) 声或 \(2\) 声对应的声音(即敲击一次 \(\text{L}\) 会听到 \(\text{"L"}\) 或 \(\text{"LL"}\),敲击一次 \(\text{R}\) 会听到 \(\text{"R"}\) 或 \(\text{"RR"}\))。

给定实际敲击的指令序列字符串 \(p\) 与听到的声音字符串 \(s\),判断 \(s\) 是否可能由 \(p\) 产生。


核心思路

1. 行程编码(RLE 分块)

观察题目规则,敲击是严格按顺序发生的,且每次敲击只可能将单个字符复制成 \(1\) 个或 \(2\) 个相同的字符。

因此,连续相同字符组成的字符块(Block)的种类与相对先后顺序在敲击转换过程中保持不变。

我们可以将字符串 \(p\) 和 \(s\) 分别做一次连续字符的行程压缩:

  • 设 \(p\) 分解为若干连续段:

    \[K_1 = [(c_1, a_1), (c_2, a_2), \dots, (c_m, a_m)] \]

    其中 \(c_i \in \{\text{'L'}, \text{'R'}\}\) 为字符类型,\(a_i\) 为该字符连续出现的次数。
  • 设 \(s\) 分解为若干连续段:

    \[K_2 = [(c'_1, b_1), (c'_2, b_2), \dots, (c'_k, b_k)] \]

    其中 \(c'_i \in \{\text{'L'}, \text{'R'}\}\) 为字符类型,\(b_i\) 为该字符连续出现的次数。

2. 合法性判定条件

要使 \(s\) 能由 \(p\) 合法产生,必须同时满足以下三个充要条件:

  1. 块的总数必须相同:

    \[m = k \]

    如果块的个数不等,说明鼓点交替的轮次发生了变化,一定不合法。

  2. 对应块的字符种类必须一致:

    \[\forall i \in [1, m], \quad c_i = c'_i \]

    左右鼓不能相互替换。

  3. 每个块的长度落在理论上下界之内:
    对于 \(p\) 中连续敲击了 \(a_i\) 次的字符,每次敲击产生 \(1\) 或 \(2\) 个声响,因此最终在 \(s\) 中产生的对应字符数 \(b_i\) 的范围必然满足:

    \[\forall i \in [1, m], \quad a_i \le b_i \le 2a_i \]

若所有块均满足上述约束,则答案为 YES;否则为 NO。


说人话

其实就是开个结构体记录每个字符出现的位置和连续出现的个数,和字符类别,后面就比较一下就可以了,如果字符不同就NO,如果s的某个字符连续个数小于p或大于2*p就是错的,因为一个字符可以产生两个相同字符

复杂度分析

  • 时间复杂度:对 \(p\) 与 \(s\) 均只做一次单层循环线性扫描分块,随后做一次长度不超过 \(\vert{}p\vert{}\) 的比对遍历。单组测试点复杂度为 \(\mathcal{O}(\vert{}p\vert{} + \vert{}s\vert{})\)。对于总和 \(\sum \vert{}s\vert{} \le 2 \cdot 10^5\),运行时间在几十毫秒以内,远低于 \(2.0\text{ s}\) 时限。
  • 空间复杂度:存储块结构体动态数组,开销为 \(\mathcal{O}(\vert{}p\vert{} + \vert{}s\vert{})\),空间复杂度极低。

C++ 代码实现

#include <iostream>
#include <string>
#include <vector>

using namespace std;

// 记录连续相同字符块的信息
struct Block {
    int position; // 块序号
    char c;       // 字符 ('L' 或 'R')
    int cnt;      // 连续出现次数
};

void solve() {
    string p, s;
    cin >> p >> s;
    
    vector<Block> k1, k2;
    
    // 对 p 进行连续字符分块
    int cnt = 1;
    int position = 1;
    for (int i = 0; i < (int)p.length() - 1; i++) {
        if (p[i] == p[i + 1]) {
            cnt++;
        } else {
            k1.push_back({position, p[i], cnt});
            position++;
            cnt = 1;
        }
    }
    k1.push_back({position, p.back(), cnt});

    // 对 s 进行连续字符分块
    cnt = 1;
    position = 1;
    for (int i = 0; i < (int)s.length() - 1; i++) {
        if (s[i] == s[i + 1]) {
            cnt++;
        } else {
            k2.push_back({position, s[i], cnt});
            position++;
            cnt = 1;
        }
    }
    k2.push_back({position, s.back(), cnt});

    // 条件 1: 块数量不相等直接判 NO
    if (k1.size() != k2.size()) {
        cout << "NO\n";
        return;
    }

    // 逐块验证合法性
    for (int i = 0; i < (int)k1.size(); i++) {
        // 条件 2: 字符必须一致
        if (k1[i].c != k2[i].c) {
            cout << "NO\n";
            return;
        }
        // 条件 3: 字符数量必须满足 a_i <= b_i <= 2 * a_i
        if (k2[i].cnt < k1[i].cnt || k2[i].cnt > 2 * k1[i].cnt) {
            cout << "NO\n";
            return;
        }
    }

    cout << "YES\n";
}

int main() {
    // 快速 IO,避免多测试用例下 cin/cout 瓶颈
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int t;
    if (cin >> t) {
        while (t--) {
            solve();
        }
    }
    return 0;
}
posted @ 2026-09-28 15:08  hermanO  阅读(7)  评论(0)    收藏  举报