题目描述
难度分:2600
输入T(≤103)表示T组数据。所有数据的n之和≤2×105。
每组数据输入n(3≤n≤2×105)和两个长为n的01
字符串a和b。
每次操作,你可以选择一个值为1的a[i],然后把a[i−1]和a[i+1]翻转(异或 1)。如果对最左边的i操作,就只把a[i+1]翻转;如果对最右边的i操作,就只把a[i−1]翻转。
注意操作不会翻转a[i]。
能否把a变成b?输出YES
或NO
。
输入样例1
2
4
0101
0100
3
000
010
输出样例1
YES
NO
输入样例2
5
7
0101011
1111010
5
11111
00000
4
1101
1101
6
101010
100100
3
000
000
输出样例2
NO
NO
YES
YES
YES
算法
找规律+前缀异或和
智力题,做不了一点。首先原序列是不好考虑的,我们考虑前缀异或序列(这一步就需要经验,有些题目会考虑差分序列等),s1是a的前缀异或和数组,s2是b的前缀异或和数组。然后发现以下规律,将操作分为两种:
- 如果选择的i>1,则每次操作就相当于交换s1[i−1]和s1[i],根据冒泡排序的理论,只需要s1和s2中0的数量相等即可(0的数量相等,长度也相等,因此1的数量也相等)。
- 如果选择了i=1进行操作,前面没有s1[0]和s1[1]交换,需要进一步分析。这时候发现如果a[1]=
1
,进行这一步操作就相当于s1[1]不变,s1[2:n]全部异或上1(反转)。这时候我们只需要操作前的s1中“0的个数加1”等于“s2中1的个数”即可,这样就能保证操作完后两个前缀异或序列s1和s2中有相同的0和1。如果a[1]=0
,那就通过操作1(i>1的情况)换个1到前面来再判断。
复杂度分析
时间复杂度
算法整体就是在统计s1和s2中0或1的个数,因此线性复杂度就能得到结果,时间复杂度为O(n)。
空间复杂度
开辟了前缀异或和的数组s1和s2,额外空间复杂度为O(n)。
C++ 代码
#include <bits/stdc++.h>
using namespace std;
const int N = 200010;
int t, n, s1[N], s2[N];
char a[N], b[N];
int main() {
scanf("%d", &t);
while(t--) {
scanf("%d", &n);
scanf("%s", a + 1);
scanf("%s", b + 1);
for(int i = 1; i <= n; i++) {
s1[i] = s1[i - 1] ^ (a[i] - '0');
}
for(int i = 1; i <= n; i++) {
s2[i] = s2[i - 1] ^ (b[i] - '0');
}
if(count(s1 + 1, s1 + n + 1, 0) == count(s2 + 1, s2 + n + 1, 0)) {
puts("YES");
}else if(count(s1 + 1, s1 + n + 1, 0) == n) {
puts("NO");
}else if(count(s1 + 1, s1 + n + 1, 0) + 1 == count(s2 + 1, s2 + n + 1, 1)) {
puts("YES");
}else {
puts("NO");
}
}
return 0;
}