其实本来想讲从easy−>hard的
但是自己的算法可以过两个版本?那就只讲这种方法好了
构造题,每次可以选前缀位运算取反,然后翻转这个前缀
相信棘手的就是这个翻转前缀,使得我们的操作变得复杂,不可控
但是这是一道构造题,只需要在2n的步骤内完成即可
那设想一下,如果前缀都是相同,翻转不就不需要考虑了吗?
具体实现分为两个步骤
Ⅰ.把a串变成00000...00000的形式
Ⅱ.把00000..00000变成b串的形式
比如当a串等于01011时(下标从1起),如何变成00000?
一、因为第一位是0,不用管
二、第二位是1,那么先对[1,1]操作,于是现在变成11011
然后对[1,2]操作,于是现在变成00011
三、第三位是0,不用管
四、第四位是1,那先对[1,3]操作,于是现在变成11111
然后对[1,4]操作,于是现在变成00001
..............
总之,一旦a串的x位置是1,就操作[1,x−1]使得这个区间变成1
再操作[1,x]区间使得这个区间都变成0
如何从00000变成b串11001?
这个就不难想了,和上面差不多的思想
从高位往低位看,如果b串的x位置是1
那么操作[1,x]使得a串[1,x]都是1
再操作[1,x−1],使得a串的[1,x−1]还原为0
一直重复下去.......
那么有代码
if( a[1]=='1' ) vec.pb(1);
for(int i=2;i<=n;i++)
{
if( a[i]=='0' ) continue;
else vec.pb(i-1),vec.pb(i);//操作[1,i-1]变成1,再操作[1,i]都变0
}
for(int i=n;i>=2;i--)
{
if(b[i]=='0') continue;
else
{
vec.pb(i);
vec.pb(i-1);
}
}
if( b[1]=='1' ) vec.pb(1);
但是就可以发现这样写做多的操作次数可能超过了2n次
观察一下上面的操作过程,其实我们可以对操作去重
因为如果进行了偶数次操作区间[1,i],等于没操作,直接去掉
如果进行了奇数次操作区间[1,i],只保留一次
所以在a−>00000时,每个位置最多操作一次,也就是n次
在00000−>b时,每个位置最多操作一次,也就是n次,加起来2n次
#include <bits/stdc++.h>
using namespace std;
#define pb push_back
int t,n;
char a[200009],b[200009];
typedef pair<int,int>p;
vector<int>vec,s;
int main()
{
cin >> t;
while(t--)
{
vec.clear();s.clear();
cin >> n >> (a+1) >> (b+1);
int flag=0;
for(int i=1;i<=n;i++)
if(a[i]!=b[i]) flag=1;
if( flag==0 )
{
cout<<0<<endl;
continue;
}
if( a[1]=='1' ) vec.pb(1);
for(int i=2;i<=n;i++)
{
if( a[i]=='0' ) continue;
else vec.pb(i-1),vec.pb(i);
}
for(int i=n;i>=2;i--)
{
if(b[i]=='0') continue;
else
{
vec.pb(i);
vec.pb(i-1);
}
}
if( b[1]=='1' ) vec.pb(1);
int last=vec[0],temp=1;
for(int i=1;i<vec.size();i++)
{
if( vec[i]!=last )
{
if( temp%2==1 ) s.pb(last);
temp=1,last=vec[i];
}
else temp++;
}
if( temp%2==1 ) s.pb(last);
cout<<s.size()<<" ";
for(int i=0;i<s.size();i++) cout<<s[i]<<" ";
cout<<endl;
}
}
emmm讲完啦,希望对你有帮助,一起进步呀!