<span>题解 CF1399D 【Binary String To Subsequences】</span>

题目链接:http://codeforces.com/contest/1399/problem/D
题目描述:

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⋅\(10^4\)) — the number of test cases. Then t test cases follow.
The first line of the test case contains one integer n (1≤n≤2⋅ \(10^5\)) — 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⋅ \(10^5\) (∑n≤2⋅\(10^5\)).

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 \(a_1\),\(a_2\),…,\(a_n\) (1≤\(a_i\)≤k), where \(a_i\) is the number of subsequence the i-th character of s belongs to.
If there are several answers, you can print any.

最开始的朴素做法(这种做***TLE):
用一个数组c来存储每个子序列最后的数字,在遍历数组a的同时使\(a_i\)遍历数组c,使其接上满足题意的子序列。如果没有找到,\(a_i\)单独作为一个子序列。用\(b_i\)来记录\(a_i\)是属于第几个子序列的,因为是保序的,只需令\(b_i\)=j+1就行了。

#include<iostream>
#include<vector>
using namespace std;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t;
    cin>>t;
    while(t--)
    {
        int n;
        char x;
        cin>>n;
        vector<int> a(n+5),b(n+5),c;
        for(int i=1;i<=n;i++)
        {
            cin>>x;
            a[i]=x-'0';
        }
        
        c.push_back(a[1]);
        b[1]=1;
        for(int i=2;i<=n;i++)
        {
            int len=c.size();
            bool flag=true;
            for(int j=0;j<len;j++)
            {
                if(a[i]+c[j]==1)
                {
                    c[j]=a[i];
                    b[i]=j+1;
                    flag=false;
                }
                if(flag==false)
                break;
            }
            if(flag==true)
            {
                c.push_back(a[i]);
                b[i]=len+1;
            }
        }
        cout<<c.size()<<endl;
        //cout<<"n=="<<n<<endl;
        for(int i=1;i<=n;i++)
           cout<<b[i]<<" ";
        cout<<endl;
        c.clear();
        //cout<<"c.size()=="<<c.size()<<endl;
    }
    return 0;
}

正确做法:上面的朴素做***TLE的根本原因在于对于每一个\(a_i\)都要遍历一遍数组c,这样时间复杂度就上去了,而实际上我们只需要对每个\(a_i\)判断当前已经存在的子序列中是否有以1结尾的子序列或以0结尾的子序列即可。而不需要每次遍历。因此我们用两个数组分别来存取以0或1结尾的子序列就可以了。这时为了确定\(b_i\)的值,我们需要对两个数组中所存储的值进行修改,应该存储的是每个子序列的编号,而不再是0或1了。

#include<iostream>
#include<vector>
#include<string>
using namespace std;
string s;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t;
    cin >> t;
    while (t--)
    {
        int n;
        cin >> n >> s;
        vector<int> b(n + 5), c0, c1;
        for (int i = 0;i < n;i++)
        {
            int newpos;
            if (s[i] == '0')
            {
                newpos = c0.size() + c1.size();
                if (c1.empty())
                {
                    c0.push_back(newpos);
                }
                else
                {
                    newpos = c1.back();
                    c1.pop_back();
                    c0.push_back(newpos);
                }
            }
            else
            {
                newpos = c0.size() + c1.size();
                if (c0.empty())
                {
                    c1.push_back(newpos);
                }
                else
                {
                    newpos = c0.back();
                    c0.pop_back();
                    c1.push_back(newpos);
                }
            }
            b[i] = newpos + 1;
        }
        cout << c0.size() + c1.size() << endl;
        for (int i = 0;i < n;i++)
            cout << b[i] << " ";
        cout << endl;
    }
    return 0;
}
全部评论

相关推荐

难怪不开摄像头,全是简单的性格题,比大疆友善多了
NULL10086:今早上发的测评,我这还没做呢,官网上已经显示挂了
投递大疆等公司7个岗位
点赞 评论 收藏
分享
来个厂收我吧:首先,市场侧求职我不是很懂。 但是,如果hr把这份简历给我,我会觉得求职人不适合做产品经理。 问题点: 1,简历的字体格式不统一,排版不尽如人意 2,重点不突出,建议参考star法则写个人经历 3,印尼官方货币名称为印度尼西亚卢比(IDR),且GMV690000印尼盾换算为305人民币,总成交额不高。 4,右上角的意向职位在发给其他公司时记得删除。 5,你所有的经历都是新媒体运营,但是你要投市场营销岗位,jd和简历不匹配,建议用AI+提示词,参照多个jd改一下经历内容。 修改建议: 1,统一字体(中文:思源黑体或微软雅黑,英文数字:time new romans),在word中通过表格进行排版(b站学) 2,校招个人经历权重:实习经历=创业经历(大创另算)>项目经历>实训经历>校园经历 3,请将项目经历时间顺序改为倒序,最新的放最上方。 4,求职方向不同,简历文字描述侧重点也需要不同。
点赞 评论 收藏
分享
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务