1 条题解
-
0
题目回顾
我们有两个鼓,左鼓敲击记为
L,右鼓敲击记为R。由于奇异力量的影响,一次敲击听到的声音可能是一声,也可能是两声。也就是说,实际的敲击序列 中的每个字符都可能“扩展”为 个或 个相同的字符,得到最终听到的声音序列 。给定 和 ,判断 是否可能由 按照上述规则扩展得到。
- 敲击序列 由
L和R组成,长度 。 - 听到的声音 长度 ,。
我们需要对每个测试用例输出
YES或NO。简单情况:只有一种字符
假设 完全由
L组成,长度为 。那么每个L可以变成L或LL,总长度可以在 (每个都选一声)到 (每个都选两声)之间任意取值。并且,因为所有字符都是L,所以 也只能全部是L。因此,当 只有L时, 合法的充要条件是:- 全部由
L组成; - 。
同理,若 只有
R,充要条件也类似。一般化:任意
L/R序列实际的 是由
L和R交替组成的。我们可以将 划分为若干个极大连续相同字符段(称为“块”或游程)。例如:p = LLLLLRL
它的块依次为:
LLLLL(5个L),R(1个R),L(1个L)。在扩展过程中,每个块内的字符都会扩展为相同字符,且块之间保持原有的交替顺序。一个块的长度为 ,扩展后对应块的长度 必须在 之间。同时, 中的块数必须与 中的块数完全相等,并且对应块的字符必须相同(这等价于首字符相同,因为块是交替的)。
这样,问题就转化为:
- 将 和 分别压缩成游程长度数组 和 。
- 检查是否满足:
- (块数相等);
- 和 的首字符相同(这隐含了对应块的字符全部匹配,因为块是交替的);
- 对于每个 ,有 。
此外,由长度范围自然可推出 ,所以也可先做总长度剪枝。
算法步骤
- 读入 和 ,记 。
- 快速非法判断:
- 若 或 ,直接输出
NO。 - 若 ,直接输出
NO(因为第一个块字符必须相同)。
- 若 或 ,直接输出
- 构建游程长度数组:
- 遍历 ,每当字符变化时,将当前计数存入数组 ,并重置计数;遍历结束后将最后一个计数加入 。
- 对 做同样操作得到 。
- 块数检查:若 的大小与 的大小不同,输出
NO。 - 逐块检查:对于 从 到块数,检查 。若有不满足者输出
NO。 - 全部通过则输出
YES。
示例分析
以 为例:
- ,块为:
L(长度1),R(长度1)。 - 合法的 长度必须在 之间。
- 第一个块(
L)可以扩展为长度 或 的L;第二个块(R)可以扩展为长度 或 的R。 - 组合得到:
LR、LRR、LLR、LLRR。与题目描述一致。
再以 为例:
- 块长度:。
- 的块长度为 。
- 检查:?是;?是。且块数、首字符均匹配,答案为
YES。
正确性证明
必要性:若 由 扩展得到,则每个敲击要么变成 个相同字符,要么变成 个。因此同一块内的字符必然来自同一字符的扩展,块数不会改变,且每个块的长度必然介于原长度和两倍原长度之间。首字符也必须一致。
充分性:若上述条件满足,则可按顺序对第 个块,将多余的 个字符分配到该块内的 个位置中的任意几个(每个位置最多增加 个字符),即总能构造出合法的扩展方式。因此条件充要。
复杂度分析
- 时间复杂度:对于每个测试用例,我们只需分别扫描 和 一次,提取游程长度,再做一次遍历检查。总复杂度为 。题目保证所有测试用例的 之和不超过 ,所以可以在时限内轻松完成。
- 空间复杂度:存储游程长度数组需要 的额外空间,但也可以压缩后即时比较,空间可优化到 。考虑到实现简单,通常直接使用 vector。
标准程序
#include <bits/stdc++.h> using namespace std; #define int long long void solve() { string a, b; cin >> a >> b; int n = a.size(); int m = b.size(); if (m < n || m > 2 * n || a[0] != b[0]) { cout << "NO\n"; return; } vector<int> aa, bb; int cnt = 1; for (int i = 1; i < n; i++) { if (a[i] != a[i-1]) { aa.push_back(cnt); cnt = 1; } else cnt++; } aa.push_back(cnt); cnt = 1; for (int i = 1; i < m; i++) { if (b[i] != b[i-1]) { bb.push_back(cnt); cnt = 1; } else cnt++; } bb.push_back(cnt); if (aa.size() != bb.size()) { cout << "NO\n"; return; } n = aa.size(); for (int i = 0; i < n; i++) { if (aa[i] > bb[i] || aa[i] * 2 < bb[i]) { cout << "NO\n"; return; } } cout << "YES\n"; } signed main() { ios_base::sync_with_stdio(0); cin.tie(0); int t; cin >> t; while (t--) solve(); return 0; } - 敲击序列 由
- 1
信息
- ID
- 6741
- 时间
- 2000ms
- 内存
- 256MiB
- 难度
- 3
- 标签
- 递交数
- 2
- 已通过
- 1
- 上传者