一 原题
二 分析
题意:给定一个长度不超过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; }