1 条题解
-
0
题目理解
我们有一个长度为
n的字符串,由字符'b'(白色)和'c'(黑色)组成,其中白色积木数量是黑色积木数量的k倍。每次操作需要移除连续的k+1个积木,其中恰好包含k个白色和1个黑色。关键限制:移除的积木必须是连续的(中间不能有已经被移除的积木留下的空隙)。
解题思路
核心观察
题目保证有解,我们需要找到一个合法的移除顺序。由于移除操作必须是连续的块,这提示我们可以使用栈来模拟移除过程。
算法思想
- 从左到右扫描积木,维护一个栈来记录当前还未被移除的积木编号
- 维护前缀和:记录栈中每个位置的白色和黑色积木计数
- 检查移除条件:当栈的大小 ≥
k+1时,检查栈顶的k+1个积木:- 如果这
k+1个积木中恰好有1个黑色和k个白色 - 那么这就是一个合法的操作,可以将这
k+1个积木移除
- 如果这
- 逆序输出:由于移除操作可能有依赖关系,需要从后往前输出操作序列
代码详解
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <vector> #include <string> using namespace std; const int N = 1e6 + 10; int st[N], tp; // 栈和栈顶指针 vector<int> ans[N]; // 存储每个操作移除的积木编号 int cntb[N], cntc[N]; // 栈中前缀和:cntb[i]表示栈中前i个积木的白色数量,cntc[i]表示黑色数量 int main() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); int n, k; cin >> n >> k; string s; cin >> s; s = " " + s; // 让字符串下标从1开始 int fin = 0; // 记录操作数量 for (int i = 1; i <= n; i++) { // 将当前积木压入栈中 st[++tp] = i; // 更新前缀和 cntc[tp] = cntc[tp - 1]; cntb[tp] = cntb[tp - 1]; if (s[i] == 'c') cntc[tp]++; // 黑色积木计数 else cntb[tp]++; // 白色积木计数 // 检查是否可以移除栈顶的k+1个积木 if (tp > k && cntc[tp] - cntc[tp - k - 1] == 1 && // 栈顶k+1个积木中恰好有1个黑色 cntb[tp] - cntb[tp - k - 1] == k) { // 栈顶k+1个积木中恰好有k个白色 fin++; // 新增一个操作 // 记录这个操作移除的积木编号 for (int j = tp - k; j <= tp; j++) ans[fin].emplace_back(st[j]); // 从栈中移除这k+1个积木 tp -= (k + 1); } } // 逆序输出操作序列 for (int i = fin; i >= 1; i--) { for (auto j : ans[i]) cout << j << " "; cout << "\n"; } return 0; }关键点解释
1. 为什么使用栈?
- 栈天然保持了积木的原始顺序
- 移除操作总是发生在栈顶,符合"连续无空隙"的要求
- 栈的LIFO特性与移除操作的嵌套关系相吻合
2. 前缀和的作用
cntc[tp] - cntc[tp - k - 1] == 1 // 检查黑色数量 cntb[tp] - cntb[tp - k - 1] == k // 检查白色数量这通过前缀和差值快速判断栈顶
k+1个积木的颜色分布是否符合要求。3. 为什么需要逆序输出?
考虑操作之间的依赖关系:
- 后执行的操作可能依赖于先执行的操作创造的"连续"条件
- 栈的移除顺序是从内层到外层,但实际执行需要从外层到内层
- 逆序输出保证了操作序列的合法性
示例分析
以样例为例:
输入: n=12, k=2, s="ccbcbbbbbbcb"执行过程:
- 扫描到位置8时,栈顶3个积木满足条件(1黑2白),记录操作并移除
- 继续扫描,在适当位置发现其他满足条件的连续块
- 最终得到4个操作,逆序输出
复杂度分析
- 时间复杂度:O(n),每个积木入栈出栈各一次
- 空间复杂度:O(n),用于存储栈和答案
学习要点
- 栈的巧妙应用:用栈维护连续段,处理嵌套的移除操作
- 前缀和优化:快速判断区间内的颜色数量
- 逆序思维:存储顺序与输出顺序相反,解决操作依赖问题
- 贪心思想:一旦发现合法的连续块就立即移除,保证解的构造
这种解法充分利用了题目保证有解的条件,通过简单的栈操作就能构造出合法的操作序列。
- 1
信息
- ID
- 3911
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者