CF #661 Div.3 1399D Binary String To Subsequences

分类:队列、模拟

题意:
给出一串01序列,分割这一序列,使分割成的序列没有连续的0或1,
最少能分割成几个序列,并依次输出每个字符所在的序列编号

思路:

  1. 使用两个队列 q0, q1 分别存储 0 和 1 的下标,
    使用数组 pos 存储第 i 个字符所在的子序列编号,
    使用 num 记录当前子序列数量

  2. 遍历字符串,若字符为 0,则判断队列 q1 是否为空,
    - 若 q1 不为空,说明当前存在子序列以 1 结尾,不用另外开辟一个子序列,直接接上去就行了。(接上去的操作:取出 q1 队首元素的下标 id=q1.front();删除队首元素 q1.pop();令这个 0 的子序列编号 pos[i] 等于 q1 队首的 1 的子序列编号 pos[id];将这个 0 的下标存入队列 q0 q0.push(i) .)
    - CF #661 Div.3 1399D Binary String To Subsequences_第1张图片

    - 若 q1 为空,说明当前不存在子序列以 1 结尾,不能直接接上,需要开辟一个新的子序列来存储这个 0 。(开辟新子序列操作:令这个 0 的子序列编号为 num+1 pos[i]=++num;将这个 0 的下标存入队列 q0 q0.push(i) .)
    - CF #661 Div.3 1399D Binary String To Subsequences_第2张图片

  3. 若字符为1,同理

  4. 最后输出 num,遍历输出各字符的子序列编号 pos[i]

#include 
using namespace std;

const int maxn=2e5+7;
char str[maxn];
int pos[maxn];

int main()
{
    int T; scanf("%d", &T);
    while(T--){
        int n; scanf("%d", &n);
        scanf("%s", str+1);
        queue<int> q0, q1;  // 存子序列尾
        int num=0;

        for(int i=1; i<=n; i++){
            if(str[i]=='0'){
                if(!q1.empty()){  // q1不为空
                    int id=q1.front();  // 取出下标
                    q1.pop();  // 删除元素(被接上了,不是序列尾部了,所以要在队列中删除)
                    pos[i]=pos[id];  // 子序列编号相等
                    q0.push(i);  // 是新的尾部,加入队列
                }
                else{  // q1为空
                    pos[i] = ++num;  // 开辟新序列
                    q0.push(i);  // 是新的尾部,加入队列
                }
            }
            else{
                if(!q0.empty()){
                    int id=q0.front();
                    q0.pop();
                    pos[i]=pos[id];
                    q1.push(i);
                }
                else{
                    pos[i] = ++num;
                    q1.push(i);
                }
            }
        }

        cout<<num<<endl;
        for(int i=1; i<=n; i++){
            if(i==1) cout<<pos[i];
            else cout<<' '<<pos[i];
        }
        cout<<endl;
    }

    return 0;
}

你可能感兴趣的:(2020暑假补题记录)