首先s[i]==t[i]的都可以不用管。(因为我们每次选的是子序列,完全可以跳过这些)
我们把s[i]!=t[i]的部分提取出来,让s[i]==1的设为1,否则设为0.
得到一个01串。
比如:
0111000001 1000001110
转化为:01110001 (目标是让所有01互换位置)
每次可选取一个子序列,然后让所有数往右移动(最右边到最左边)
我们发现:
一次操作可以让所有101010这样的01交替串,的01互换位置,然后就可以不管他们(因为此时s[i]==t[i])
一次操作也可以让所有010101这样的01交替串,的01互换位置,然后就可以不管他们(因为此时s[i]==t[i])
所以问题转化为了:寻找01交题串的个数。
比如01110001 :
1258是一个01交替串:0101
36是一个01交替串:10
剩下47是一个01交替串:10
需要操作三次。
找01交替串就很简单了,直接类比括号序列查找即可。
官方题解给的是:
查找1-n的最大字段和,就是结果。(不过这里把0改成了-1)
比如刚才的样例转化为:-1 1 1 1 -1 -1 -1 1
最大子段和为3.
这种思路可以查找任意l-r之间的操作数。
正确性证明:
最大子段和其实就是01交题串的个数。
最大子段和中:-1 1 成对先去掉不管,剩余的1中任意两个1都不可能再一次序列中同时变为0.
因为此时任意两个1中间都没有-1 去凑成01交替串。
由此证明最大字段和是 答案的下界(即答案大于等于最大字段和)
再考虑:
剩余的每个1都能与非最大子段和中的-1配对,(再连带上最大字段和中的 1,-1对)在一次操作中完成。
所以操作不会多于最大子段和中空闲1的数量。
即证明了最大字段和是答案的上界。
由此得证。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
const double PI= acos(-1.0);
const int M = 1e6+7;
/*
int head[M],cnt=1;
void init(){cnt=1,memset(head,0,sizeof(head));}
struct EDGE{int to,nxt,w;}ee[M*2];
void add(int x,int y,int w){ee[++cnt].nxt=head[x],ee[cnt].w=w,ee[cnt].to=y,head[x]=cnt;}
*/
char s[M],t[M];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n;
cin>>n;
cin>>(s+1)>>(t+1);
int a=0,b=0,na=0,nb=0;
for(int i=1;i<=n;i++)
{
if(s[i]=='1')na++;
if(t[i]=='1')nb++;
}
if(na!=nb)
{
cout<<-1<<endl;
return 0;
}
int ans=0;
//a: 前面以0结尾串的个数,b:前面以1结尾串的个数
for(int i=1;i<=n;i++)
{
if(s[i]=='1'&&t[i]=='0')
{
if(a)a--,b++;//把当前1加到前面 以0结尾的串后面
else b++,ans++;//前面无0结尾的串了,再开一个新串
}
if(s[i]=='0'&&t[i]=='1')
{
if(b)b--,a++;
else a++,ans++;
}
}
cout<<ans<<endl;
return 0;
}