题意

一个01字符串,可对其非空子序列进行操作:如果任意两个相邻的两位不同,可将其01反置;至少几次操作可让该01字符串任意相邻两位都不同。

关键词

构造,

题解

如果想让01字符串任意相邻的两位不相同,也就两种结果:010101...01,101010...10。 那就举1010...10:

10111001111101
10101010101010

观察这两个字符串,首先与1010...10相同的部位,就可以不用看。因为我们要求最少操作次数,对正确的部分实现两次操作,结果是毫无变化,所以没意义。所以只需要看错误的地方,将其组成原字符串的一个子序列。

1011101

因为想要进行01反置,需要其子序列也要任意相邻两位不同。从第一个看(‘1’),构成一个子序列,数量加1。

1

第二个0,需要接在1后面,恰好有一个1结尾的,第三个1,同理

101

第四个1,需要接在0结尾的,但是并没有,所以新开一个子序列,数量加1

101
1

接下来同理

10101
1
1

因为子序列数量,就是操作的数量,其次子序列是因为看是否满足条件而被迫慢慢加的,所以数量一定是最少的。 代码实现:观察子序列,只要末尾是0还是1重要,前面对于是否能接上0或1没关系,其次我们只需要记住以0结尾的数量,和以1结尾的数量就行,数量大于0,就说明有,可以接上0或1。因此用c1,c0来表示,如果为1且c0!=0,则c0--,c1++,否则只要c1++,0则同理,最后ans=c1+c0。

#include<bits/stdc++.h>
using namespace std;
int cord(string s){
    int c1=0,c0=0;
    for(int i=0;i<s.size();i++){
        if(s[i]=='1'){
            if(c0==0) c1++;
            else {
                c1++;c0--;
            }
        }
        else{
            if(c1==0) c0++;
            else{
                c1--;c0++;
            }
        }
    }
    return c1+c0;
}
void slove(string k,int n){
    string s1,s2;
    for(int i=0;i<n;i++){
        if(i%2==1&&k[i]!='1') s1+=k[i];
        else if(i%2==0&&k[i]!='0') s1+=k[i];
    }
    for(int i=0;i<n;i++){
        if(i%2==1&&k[i]!='0') s2+=k[i];
        else if(i%2==0&&k[i]!='1') s2+=k[i];
    }
    int ans=min(cord(s1),cord(s2));
    cout<<ans<<endl;
}
int main(){
    int t;
    cin>>t;
    for(int i=1;i<=t;i++){
        int n;
        cin>>n;
        string k;
        cin>>k;
        slove(k,n);
    }
}