注:解法有参考官方题解

题目描述

小芳拿到了一个仅由字符 0 和 1 组成的长为 n 的字符串 s,他可以进行任意次如下操作:  选择 s 的一个非空子序列 [1] ,该子序列任意两个相邻元素都不相同,将该子序列进行 01 反置 [2] 。 小芳想知道,最少需要多少次操作才能使得 s 中任意两个相邻元素都不相同?

【名词解释】 子序列 [1] :从原序列中删除任意个(可以为零,也可以为全部)元素,且保持剩余元素相对顺序不变得到的新序列。 01 反置 [2] :同时将字符串中的 0 变成 1, 1 变成 0。

题解如下:

题目要求最后要使s中任意两个相邻元素都不相同,那么s最后只可能时01010101的形式或10101010的形式,我们只需要把和01010101(10101010同理)不同的部分抽出来(设抽出来的部分为str),看最少需要几次可以把抽出来的部分清空,最多需要str.size()次,但是我们压缩所需要的次数(每次可按010101...01,或101010...1010的形式减少多个),但这不意味着最少需要的次数即为str中最长连续的字串的长度,具体看例子:1111100011111,如果抽了三次101,那么它会变成1111,这意味着我们还需要4次才能解决,而最长连续字串仅为5,故不成立,经过观察,我们可以发现,需要的最少次数即为区间最大子段和,对str扫两遍,一遍str[i]为1时加1,为0时减一,第二遍相反,每一遍取区间最大字段和,最后输出两遍中较小的即可。 代码如下:

ll F(string s)
{
	ll cnt=0,num,mx=0;
	for(ll i=0;i<(ll)s.size();i++)
	{
		if(s[i]=='1')cnt++;
		else cnt--;
        mx=max(mx,cnt);
		if(cnt<0)cnt=0;
	}
	num=mx;
	mx=cnt=0;
	for(ll i=0;i<(ll)s.size();i++)
	{
		if(s[i]=='0')cnt++;
		else cnt--;
        mx=max(mx,cnt);
		if(cnt<0)cnt=0;
	}
	return max(num,mx);
}
void solve()
{
	ll n,mx=1,cnt=1,ans;
	string str,s1;
	cin>>n>>str;
	for(ll i=0;i<n;i++){
		if((i&1)&&str[i]=='1')s1+=str[i];
		if(!(i&1)&&str[i]=='0')s1+=str[i];
	}
	ans=F(s1);
	s1="";
	for(ll i=0;i<n;i++){
		if((i&1)&&str[i]=='0')s1+=str[i];
		if(!(i&1)&&str[i]=='1')s1+=str[i];
	}
	ans=min(ans,F(s1));
	cout<<ans<<endl;
}

如有错误烦请指正谢谢