题目大意:

一个仅由0 1组成的字符串s可以进行任意次操作:选择s 的一个非空子序列,该子序列任意两个相邻元素都不相同,将该子序列进行 01 反置。求最少需要进行多少次操作。

子序列:从原序列中删除任意个(可以为零,也可以为全部)元素,且保持剩余元素相对顺序不变得到的新序列。

01 反置:同时将字符串中的 0 变成 1,1 变成 0。

解题思路:

对于一个0 1串我们有两种改法使其成为任意两个相邻元素都不同的串,一种是010101···还有一种是101010···我们需要分别算一下改成其中一种所需要的操作数,并进行比较,取小。

当我们确定改法后,就可以对这个串进行简化了,因为固定的位置就只能是0或1,当这个位置是正确的,就不需要进行操作了,否则就还需要消耗一次操作数将其再次反转才能变成正确的。故而,我们可以直接将这个串中正确的0或1删除(是否连续不重要,只要相对位置不变,就不会影响到结果)剩下的错误的位置及数字,我们需要对这些数字全部进行一次反转,并且取尽量少的子串去进行反转。

例如错误的子串合并起来是1011101011,第一位1作为子串①,第二位0可以接在子串①后面一起反转,这样可以减少反转次数,同理第三位1也可以接在字串①后面,此时的子串①变成了101,这时候遇到第四位的1,它就不可以接在子串①后面了,否则子串①就变成了1011,反转后不符合要求,因此这一位1可以作为子串②,接着第五位1仍旧无法接在①或②后面,只能再开一个子串③,第六位0,因为现有的三个子串都是以1结尾的,所以我们可以任选其中一个让它加入,使其中一个子串变成以0结尾的,这时候以1结尾的子串有两个,以0结尾的子串有一个,同理,后面都进行这个操作。最终将以0结尾和以1结尾的子串都加起来就是需要操作的次数。

参考代码:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod=1e9+7;
int rev(string s){
	int c1=0,c0=0;
	for(int i=0;i<s.size();i++){
		if(s[i]=='1'){
			if(c0==0) c1++;
			else{
				c0--;
				c1++;
			}
		}
		else{
			if(c1==0) c0++; 
			else{
				c1--;
				c0++;
			}
		}
	}
	return c1+c0;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	ll t;
	cin>>t;
	while(t--){
		ll n;
		cin>>n;
		string s;
		cin>>s;
		string s1,s2;
		for(int i=0;i<n;i++){
			if((i%2==0&&s[i]=='1')||(i%2==1&&s[i]=='0')) s1.push_back(s[i]);
		}
		for(int i=0;i<n;i++){
			if((i%2==0&&s[i]=='0')||(i%2==1&&s[i]=='1')) s2.push_back(s[i]);
		}
		cout<<min(rev(s1), rev(s2))<<'\n';
	}
	return 0;
}