B. Ugu

B. Ugu

A binary string is a string consisting only of the characters 0 and 1. You are given a binary string s1s2sn. It is necessary to make this string non-decreasing in the least number of operations. In other words, each character should be not less than the previous. In one operation, you can do the following:

  • Select an arbitrary index 1in in the string;
  • For all ji, change the value in the j-th position to the opposite, that is, if sj=1, then make sj=0, and vice versa.

What is the minimum number of operations needed to make the string non-decreasing?

Input

Each test consists of multiple test cases. The first line contains an integer t (1t104) — the number of test cases. The description of test cases follows.

The first line of each test cases a single integer n (1n105) — the length of the string.

The second line of each test case contains a binary string s of length n.

It is guaranteed that the sum of n over all test cases does not exceed 2105.

Output

For each test case, output a single integer — the minimum number of operations that are needed to make the string non-decreasing.

Example

input

复制代码
8
1
1
2
10
3
101
4
1100
5
11001
6
100010
10
0000110000
7
0101010
复制代码

output

0
1
2
1
2
3
1
5

Note

In the first test case, the string is already non-decreasing.

In the second test case, you can select i=1 and then s=01.

In the third test case, you can select i=1 and get s=010, and then select i=2. As a result, we get s=001, that is, a non-decreasing string.

In the sixth test case, you can select i=5 at the first iteration and get s=100001. Then choose i=2, then s=111110. Then we select i=1, getting the non-decreasing string s=000001.

 

解题思路

  先说一下官方的题解。

  构造一个数组a,其中如果si=si1,则ai=0;否则如果sisi1,则ai=1。其中a1=s1。这就是一个异或的差分数组。

  如果反转位置i上的数,那么区间[i+1,n]内的数都要反转,根据差分的性质可以发现这个操作对数组a的影响是只有ai发生了改变(0110)。

  对于一个非递降的序列,可以发现对于的数组a要么全是0,要么只有一个1。全0意味着整个s都是同一类字符,只有一个1意味着这个序列是 00 ... 01 ... 111 这种形式。

  这时我们统计整个数组a(区间[1,n])中1的个数。如果字符串的首字母即s0=0,那么此时a中最多只能有一个1,其余的1我们都要通过操作变成0,因此对于这种情况的答案就max{0,cnt1}。如果字符串的首字母即s0=1,那么此时a中只能含有一个1(也就是首字符的1),其余的1(区间[2,n])我们都要通过操作变成0,因此对于这种情况的答案就cnt1

  补充,这题是怎么想到用差分的呢。实际上,当区间[i,n]内的数反转时,等价于对这个区间内的每一个数进行模2加法(加1再对得到的值模2),因此就是对某个区间内的每个数加上一个数的问题,因此想到差分。由于最终整个序列是非递降的,因此对应的差分数组应该最多包含一个1,而每次对区间[i,n]进行模2加法,等价于对应的差分数组的第i个位置进行模2加法(第n+1个位置可以忽略)。因此直接统计原始序列的差分数组中多余的1的个数就可以了。

  AC代码如下:

复制代码
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N = 2e5 + 10;
 5 
 6 char str[N];
 7 
 8 void solve() {
 9     int n;
10     scanf("%d %s", &n, str + 1);
11     
12     int cnt = 0;
13     for (int i = n; i; i--) {
14         cnt += (str[i] & 1) ^ (str[i - 1] & 1);
15     }
16     
17     int ret;
18     if (str[1] == '0') ret = max(0, cnt - 1);
19     else ret = cnt - 1;
20     
21     printf("%d\n", ret);
22 }
23 
24 int main() {
25     int t;
26     scanf("%d", &t);
27     while (t--) {
28         solve();
29     }
30     
31     return 0;
32 }
复制代码

  另外一种解法是参考其他人的。

  对于字符串s我们进行去重操作,比如有11010001011,那么去重后就是1010101,即把连续相同的一段都用这一段中的首个数字来表示,统一把操作归到首个数字。

  可以发现去重后得到的结果只有两种,要么是从0开始的01交替序列,要么是从1开始的01交替序列。

  对于从0开始的序列,很明显最优解是从第3个位置开始每一个位置都置换一次,因此答案为n2

  对于从1开始的序列,先考虑置换第1个位置,那么就会得到从0开始的01交替序列,接着从第3个位置开始置换后面每一个位置,因此这种方式的最优解为1+n2=n1。如果不置换第1个位置,那意味着整个序列要均为1,那么从第2个位置开始每个位置都要置换,因此答案就是n1。可以发现这两种方式得到的最优解是一样的,那么我们规定只要遇到1那么就对这个位置上的数进行置换。

  在实际实现中我们并不需要把去重的结果求出来,直接枚举原字符串就可以了,只要遇到1就进行置换。

  AC代码如下:

复制代码
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N = 2e5 + 10;
 5 
 6 char str[N];
 7 int s[N];
 8 
 9 void solve() {
10     int n;
11     scanf("%d %s", &n, str + 1);
12     memset(s, 0, sizeof(s));
13     for (int i = n; i; i--) {
14         s[i] = s[i + 1] + (str[i] & 1);    // 求后缀1个个数
15     }
16     
17     int ret = 0;
18     for (int i = 1, t = 0; i <= n; i++) {    // t代表置换的累加次数
19         if (!t && s[i] == n - i + 1) break;    // 如果置换了偶数次,并且[i, n]中全为1,那么就不用再置换了
20         if (t && !s[i]) break;                // 如果置换了奇数次,并且[i, n]中全为0(等价于置换后全为1),也不用置换了
21         if ((str[i] & 1) ^ t) ret++, t ^= 1;    // 遇到1就置换
22     }
23     
24     printf("%d\n", ret);
25 }
26 
27 int main() {
28     int t;
29     scanf("%d", &t);
30     while (t--) {
31         solve();
32     }
33     
34     return 0;
35 }
复制代码

 

参考资料

  Codeforces Round #830 (Div. 2) Editorial:https://codeforces.com/blog/entry/108327

  Codeforces Round #830 (Div. 2)A、B、C1、D1(C2、D2待补):https://zhuanlan.zhihu.com/p/576542239

  差分的应用:https://www.cnblogs.com/onlyblues/p/16282454.html

posted @   onlyblues  阅读(107)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示