Codeforces1399D题解

题面

D. Binary String To Subsequences
time limit per test2 seconds
memory limit per test256 megabytes
inputstandard input
outputstandard output
You are given a binary string s consisting of n zeros and ones.

Your task is to divide the given string into the minimum number of subsequences in such a way that each character of the string belongs to exactly one subsequence and each subsequence looks like “010101 …” or “101010 …” (i.e. the subsequence should not contain two adjacent zeros or ones).

Recall that a subsequence is a sequence that can be derived from the given sequence by deleting zero or more elements without changing the order of the remaining elements. For example, subsequences of “1011101” are “0”, “1”, “11111”, “0111”, “101”, “1001”, but not “000”, “101010” and “11100”.

You have to answer t independent test cases.

Input
The first line of the input contains one integer t (1≤t≤2⋅104) — the number of test cases. Then t test cases follow.

The first line of the test case contains one integer n (1≤n≤2⋅105) — the length of s. The second line of the test case contains n characters ‘0’ and ‘1’ — the string s.

It is guaranteed that the sum of n does not exceed 2⋅105 (∑n≤2⋅105).

Output
For each test case, print the answer: in the first line print one integer k (1≤k≤n) — the minimum number of subsequences you can divide the string s to. In the second line print n integers a1,a2,…,an (1≤ai≤k), where ai is the number of subsequence the i-th character of s belongs to.

If there are several answers, you can print any.

Example
input
4
4
0011
6
111111
5
10101
8
01010000
output
2
1 2 2 1
6
1 2 3 4 5 6
1
1 1 1 1 1
4
1 1 1 1 1 2 3 4

题解

定义两个栈pos0和pos1,分别存储以0结尾的子串的编号和以1结尾的子串的编号。然后按照如下的方式来处理:
如果读到0,则先判断pos1栈是否为空。
如果不为空的话,则将pos1栈顶元素赋给a[i](表示s[i]在第a[i]个子串中),这也就是说将读到的0成功接到了已有的结尾为1的任意一个子串中,加入0之后这个子串变成了以0结尾的子串,所以要先将该子串编号压入到pos0中,然后再将pos1栈顶元素弹出。
如果为空的话,表示当前读到的元素没办法接到任何一个子串后头,所以该元素就成为了一个新的子串起点。因此子串总个数k+1,然后将k+1压入到pos0中。

以上操作方法对读到元素为1时的情况处理相同。

证明为什么这种方法一定能保证符合条件的子串个数k是最小的:
因为pos0和pos1存储的是每一个子串的编号,所以读到的元素s[i]只要接到栈顶元素后面即可。所以如果栈不为空的话,总可以成为前面某一个子串的一部分。所以只有接不了前面任何一个子串的情况下,s[i]才会成为新子串的开头,所以这时再k++。(这是产生新子串的唯一情况)。因此这样处理得到的k一定是最小的。

代码

#include<bits/stdc++.h>
using namespace std;
int main(){
	int t,n,ans;
	int a[200001];
	stack<int> pos0;
	stack<int> pos1;
	string s;
	cin>>t;
	while (t--){
		  memset(a,0,sizeof(a));
		  while (!pos0.empty())
		        pos0.pop();
		  while (!pos1.empty())
		        pos1.pop();
		  //pos0.clear(); pos1.clear();
		  cin>>n;
		  cin>>s;
		  ans=0;
		  for (int i=0; i<n; i++){
		  	  switch (s[i]){
		  	         case '0':{
		  	         	if (pos1.empty()){
		  	         	   pos0.push(++ans);
		  	         	   a[i]=ans;
						}
						else{
							a[i]=pos1.top();
							pos0.push(a[i]);
							pos1.pop();
						}
						break;
					   }
					 case '1':{
					 	if (pos0.empty()){
					 		pos1.push(++ans);
					 		a[i]=ans;
						 }
						 else{
						 	a[i]=pos0.top();
						 	pos1.push(a[i]);
						 	pos0.pop();
						 }
						break;
					 }	
			  }
		  }
		  cout<<ans<<endl;
		  for (int i=0; i<n; i++)
		      cout<<a[i]<<' ';
		  cout<<'\n';
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值