传送门


这里只说 O ( n 2 ) O(n^2) O(n2)的把,也就是暴力枚举删除区间

似乎有更快的算法,以后再补


当删除某段区间后,最终剩下一段前缀和一段后缀作括号匹配

01 01 01会自动消除

对于一段序列来说 01 01 01消除的顺序是不影响最终的结果的

不妨先对前缀做括号匹配,变成 11110000... 11110000... 11110000...的形式

对后缀也做括号匹配,变成 11110000 11110000 11110000的形式

不难发现,此时能消除的只剩下前缀后面的连续零和后缀前面的连续一

所以直接预处理这两部分,还要预处理前缀自己匹配剩下的长度和后缀剩下的长度

用栈模拟括号匹配即可

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int n,top,pre[maxn],pre0[maxn],suf[maxn],suf1[maxn];
int o[maxn],z[maxn];
char a[maxn],q[maxn];
int main()
{
	cin >> n >> (a+1);
	int zero = 0;
	for(int i=1;i<=n;i++)
	{
		o[i] = o[i-1]+(a[i]=='1');
		z[i] = z[i-1]+(a[i]=='0');
		if( !top )	q[++top] = a[i],zero = ( a[i]=='0' );//栈空,没问题 
		else	
		{
			if( q[top]=='0'&&a[i]=='1' )	top--,zero--;
			else//不能消除,栈顶是0自己也是0,栈顶是1自己是0或1 
				q[++top] = a[i], zero += ( a[i]=='0' );
		}
		pre[i] = top, pre0[i] = zero;
	}
	int ans = top;
	int one = 0; top = 0;
	for(int i=n;i>=1;i--)
	{
		if( !top )	q[++top] = a[i], one = ( a[i]=='1' );
		else
		{
			if( q[top]=='1'&&a[i]=='0' )	top--,one--;
			else
				q[++top] = a[i], one += ( a[i]=='1' );
		}
		suf[i] = top, suf1[i] = one;
	}
	for(int i=1;i<=n;i++)
	for(int j=i+1;j<=n;j++)
	{
		if( o[j]-o[i-1]!=2*(z[j]-z[i-1]) )	continue;
		ans = min( ans,pre[i-1]+suf[j+1]-min( pre0[i-1],suf1[j+1])*2 ); 
	}
	cout << ans;
	return 0;
}