目录
题目:
题目描述:
给你两串长度为 n 的 01字符串 a 和 b ,
操作要求:若从开头到当前位置区间内 0 的总数和 1 的总数相同,则可以进行逆转操作( 区间内的 0 变 1 , 区间内的1 变 0 )
问能不能经过有限次操作,将 a 串转化为 b 串。
思路:
若需要逆转的连续区间 恰好 处于 前缀区间01个数相同 的两个位置之间(不用相邻),则可以通过两次操作将此段区间逆转后不影响区间外的数。(情况如下图)
绿色的线代表的是前面 1 和 0 个数相同的位置(可以进行翻转的位置),经过黄色和粉色两次操作,我们就可以将红色区间进行翻转且不影响外部。
若需要逆转的连续区间 并非恰好 处于 前缀区间01个数相同 的两个位置之间,则要么不能将区间转化完全,要么在转化的过程中会影响外部不用转化的区间,从而导致,a串必定不能转化成b串。(如下图:)
所以,我们的目标就变成了,记录前缀区间 01个数相同的位置,找 a 串需要逆转的几个连续区间是否完全对应上面记录的位置即可。
思路有了,具体操作请看AC代码:
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 3e5 + 5;
char a[N];
char b[N];
void solve()
{
bitset<N>vis;//记录前缀区间01个数相同的右端点位置
vis[0] = 1;
int n;
cin >> n;
cin >> a + 1 >> b + 1;
int sum = 0;
int flag = 1;//标志着是否能操作成功,初始默认成功
for (int i = 1; i <= n; i++)
{
if (a[i] == '0')
sum++;
else
sum--;
if (sum == 0) //当sum为0的时候说明前面0和1的个数相同
vis[i] = 1;
}
for (int i = 1; i <= n;)
{
if (a[i] == b[i])
{
i++;
continue;
}
int j = i + 1;
while (j <= n && a[j] != b[j])
j++;
if (vis[i - 1] && vis[j - 1])
i = j;
else
{
flag = 0;
break;
}
}
cout << (flag ? "YES" : "NO") << '\n';
}
int main()
{
std::ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t; cin >> t;
while (t--)
solve();
return 0;
}