一 原题
二 分析
题意:给定一个长度不超过2e5的01字符串s,zebra串的形式为0(01)*0,问s能否划分成若干子序列,每个子序列都是一个zebra串。
分析:每个zebra串中0的数量比1的数量多1,所以如果答案如果是可行,那么s中1的数量减去0的数量k就是子序列的数量。如果k<=0,肯定无解。假设有k个桶对应k个子序列,我们的任务就是把s中的每个字符放到一个桶中。桶可以分成两类:下一个字符需要0的和下一个字符需要1的。初始化时每个桶都是要0的,当0放入某个桶后,它成为需要1的那一类;当1放入某个桶后,它成为需要0的那一类。在放字符的过程中可能发生当前字符为x,而不存在需要x的桶,那么此时就可以判定无解了。最后,每个桶都应该进入需要1的状态(zebra串以0结束),如果此时还有需要0的桶,那么也无解。
其实有解的条件等价于对于s的任意前缀和后缀,其中0的数量要大于等于1的数量。满足该条件时,在上述算法中一定不会出现当前字符为x而没有需要x的桶的情况。
三 代码
/*
AUTHOR: maxkibble
LANG: c++ 17
PROB: cf 949A
*/
#include <cstdio>
#include <queue>
#include <vector>
#include <cstring>
#define pb push_back
const int maxn = 2e5 + 10;
char s[maxn];
int n, num;
std::queue<int> q0, q1;
std::vector<int> ans[maxn];
int main() {
scanf("%s", s + 1);
n = strlen(s + 1);
for (int i = 1; i <= n; i++) {
if (s[i] == '0') num++;
else num--;
}
if (num <= 0) {
puts("-1");
return 0;
}
for (int i = 0; i < num; i++) q0.push(i);
for (int i = 1; i <= n; i++) {
if (s[i] == '0') {
if (q0.empty()) {
puts("-1");
return 0;
}
int x = q0.front(); q0.pop();
q1.push(x);
ans[x].pb(i);
}
else {
if (q1.empty()) {
puts("-1");
return 0;
}
int x = q1.front(); q1.pop();
q0.push(x);
ans[x].pb(i);
}
}
if (!q0.empty()) {
puts("-1");
return 0;
}
printf("%d\n", num);
for (int i = 0; i < num; i++) {
printf("%d", ans[i].size());
for (int item: ans[i]) printf(" %d", item);
puts("");
}
return 0;
}