Codeforces Round #712 (Div. 2) B. Flip the Bits 题解
旅行传送门:https://codeforces.com/contest/1504/problem/B
B. Flip the Bits
5 10 0111010000 0100101100 4 0000 0000 3 001 000 12 010101010101 100110011010 6 000111 110100
YES YES NO YES NO
题目大意
解题思路
首先此题有几个要注意的地方:
1.每次操作变换的是字符串a中长度为i的前缀(即是对字符串a的前i个字符取异或)
2.每次操作选取的前缀中0和1的字符数相等
试想一下,如果是模拟,我们应该怎么做?
首先从末尾开始对两字符串进行逐个比较,如果ab第i个数字互异,则将a中长度为i且01字符数相等的前缀全部取异或,然后从当前位置继续比对,直至无法修改或a转换成b为止。显然,每次都修改并重新统计的话是行不通的,复杂繁琐的代码与极高的时间复杂度令人望而却步。
那么我们先统计字符串ab中数字1的个数,然后反向处理字符串,用一个指针记录后缀相同的位置,因为每次修改后前缀中匹配与不匹配的关系就互换了,所以若是进行过奇数次修改,那就从修改处开始往前搜索两字符串最初匹配的部分直至遇到不匹配的;否则就搜索不匹配的部分。如果某次操作时选取的前缀中0和1的字符数不等或ab两字符串的前缀中0或1的数目不相等,说明a不可能转换为b,如果能一直修改到底,说明可以成功转换。
笔者语文水平有限,可能文字说明比较晦涩难懂,以样例来作进一步阐释说明:
string a = 010101010101string b = 100110011010
先统计a、b中1的个数均为6
从末尾向前搜索,长度为4的后缀不同,记录i的位置为8,第一次操作:[010101010101] → [101010101010]
从i = 8的位置继续向前搜索,此时原字符串中以i为长度的前缀与b的匹配关系相反,读出原来7-8的字符匹配(奇数次操作后不匹配了),第二次操作:[10101010]1010→ [01010101]1010
从i = 6的位置继续向前搜索,异或偶数次后原字符串中以i为长度的前缀又变回最初的样子,读出5-6的字符不匹配,第三次操作:[010101]011010→ [101010]011010
从i = 4的位置继续向前搜索,奇数次,找原来匹配的3-4,第四次操作:[1010]10011010→ [0101]10011010
最终操作:[01]1010011010→ [10]0110011010
不难发现,上述操作选取的前缀中0和1的字符数都保持相等且ab两字符串的前缀中0或1的数目也相等
再举两个反例吧:
string b = 000
一开始统计后就能发现ab串中0、1数目不等,直接pass
string b = 110100
第一次操作后:a → 111000
从i = 4处往前搜索,奇数次找原来的匹配项,3-4相等,但此时两条件都不满足,说拜拜~
剩下的看代码呗,如果有疑惑或者更好的作法欢迎在评论区与小蒟交流,文笔不足之处还请见谅(✿◡‿◡)
AC代码
1 #include <bits/stdc++.h> 2 #define MAXN 300000 + 10 3 4 char s1[MAXN], s2[MAXN]; 5 6 int main(int argc, char const *argv[]) 7 { 8 int t; 9 scanf("%d", &t); 10 while (t--) 11 { 12 /** 13 * sum1 a中字符1的数目 14 * sum2 b中字符1的数目 15 * cnt 操作次数 16 */ 17 int n, sum1 = 0, sum2 = 0, flag = 1, cnt = 0; 18 scanf("%d", &n); 19 scanf("%s%s", s1 + 1, s2 + 1); //字符串偏移 20 for (int i = 1; i <= n; i++) //统计字符串ab中1的数目 21 { 22 sum1 += s1[i] - '0'; 23 sum2 += s2[i] - '0'; 24 } 25 if (sum1 != sum2) //开局不相等就可以直接/remake了 26 { 27 puts("NO"); 28 flag = 0; 29 continue; 30 } 31 for (int i = n; i; i--) //i作为字符串指针 32 { 33 if (s1[i] == s2[i]) 34 { 35 if (!(cnt % 2)) //偶数次异或操作还原,找不匹配的部分 36 { 37 sum1 -= (s1[i] - '0'); 38 sum2 -= (s2[i] - '0'); 39 continue; 40 } 41 else //奇数次操作匹配关系互换,找最初匹配的部分 42 { 43 while (s1[i] == s2[i] && i) 44 { 45 sum1 -= (s1[i] - '0'); 46 sum2 -= (s2[i] - '0'); 47 i--; 48 } 49 if (sum1 != sum2 || i - sum1 != sum1) //两个条件不满足其一都不能完成转换 50 { 51 puts("NO"); 52 flag = 0; 53 break; 54 } 55 i++, cnt++; //之前的while循环指针往前多挪了一位 56 } 57 } 58 else 59 { 60 while (s1[i] != s2[i] && i) 61 { 62 sum1 -= (s1[i] - '0'); 63 sum2 -= (s2[i] - '0'); 64 i--; 65 } 66 if (sum1 != sum2 || i - sum1 != sum1) 67 { 68 puts("NO"); 69 flag = 0; 70 break; 71 } 72 i++, cnt++; 73 } 74 } 75 if (flag) 76 puts("YES"); 77 } 78 return 0; 79 }