构造 / 贪心 - Binary String To Subsequences - CF 1399D

博客介绍了如何通过贪心算法解决一个字符串分割问题,即给定一个01串,将其分成尽可能少的子串,每个子串内不允许连续的0或1。题目给出了输入输出示例及代码实现,代码中维护了两个数组g0和g1,分别存储以0和1结尾的子串组号,通过遍历字符串动态更新这两个数组。最后输出最少的子串组数和每个字符所属的组号。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

构造 / 贪心 - Binary String To Subsequences - CF 1399D

题意:

给定一个长度为 n 的 01 串,把它分成尽量少的子串,使得每一个子串不出现连续的 0 或 1。

求最少能够分成几组子串,并对于每一个字符,输出它们属于哪一组。

输出任意一组可行解。

Input

The first line of the input contains one integer t (1≤t≤2⋅1041≤t≤2⋅10^41t2104) — the number of test cases. Then t test cases follow.

The first line of the test case contains one integer n (1≤n≤2⋅1051≤n≤2⋅10^51n2105) — 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∑n≤2⋅10^5n2105).

Output

For each test case, print the answer: in the first line print one integer k (1≤k≤n1≤k≤n1kn) — the minimum number of subsequences you can divide the string s to. In the second line print n integers a1,a2,…,an (1≤ai≤k1≤a_i≤k1aik), 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 

分析:

维护两个数组:g0g_0g0g1g_1g1

分别存储以 0 / 1 结尾的子串的组号。

然后我们遍历字符串,

  • 若遇到字符 0,则我们在以 1 结尾的子串中任选一个,把 0 加到这个子串后面即可。此时,原本以 0 结尾的子串就变成了以 1 结尾的子串。如果不存在以 1 结尾的子串,则我们需要创建一个新的以 0 结尾的子串。
  • 若遇到字符 1,同理。
  • 每一个字符处理完后,在数组 a 中记录下该字符所在的组号即可。

代码:

#include<cstdio>
#include<algorithm>
#include<vector>
#include<string>
#include<iostream>

using namespace std;

const int N = 200010;

int t, n;
char s[N];

int main()
{
	scanf("%d", &t);
	while(t --)
	{
		scanf("%d", &n);
		scanf("%s", s);
		vector<int> g0, g1;
		vector<int> a(n);
		for(int i = 0; i < n; i ++)
		{
			int g_idx = g0.size() + g1.size() + 1;
			if(s[i] == '0')
			{
				if(g1.empty()) g0.push_back(g_idx);	// new group
				else
				{
					g_idx = g1.back();	// 取出以 1 结尾的组号 
					g1.pop_back();		// 该组现在以 0 结尾 
					g0.push_back(g_idx);
				}
			}
			else
			{
				if(g0.empty()) g1.push_back(g_idx);
				else
				{
					g_idx = g0.back();
					g0.pop_back();
					g1.push_back(g_idx); 
				}
			}
			a[i] = g_idx;
		}
		
		printf("%d\n", g0.size() + g1.size());
		for(int i = 0; i < n; i ++) printf("%d ", a[i]);
		puts("");
	}

	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值