Codeforces Round 1017 (Div. 4)_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\) 合法产生,必须同时满足以下三个充要条件:
-
块的总数必须相同:
\[m = k \]如果块的个数不等,说明鼓点交替的轮次发生了变化,一定不合法。
-
对应块的字符种类必须一致:
\[\forall i \in [1, m], \quad c_i = c'_i \]左右鼓不能相互替换。
-
每个块的长度落在理论上下界之内:
对于 \(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;
}

浙公网安备 33010602011771号