B. Ugu

B. Ugu

A binary string is a string consisting only of the characters $0$ and $1$. You are given a binary string $s_1 s_2 \dots s_n$. 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 $1 \leq i \leq n$ in the string;
  • For all $j \geq i$, change the value in the $j$-th position to the opposite, that is, if $s_j=1$, then make $s_j=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$ $(1 \leq t \leq {10}^{4})$ — the number of test cases. The description of test cases follows.

The first line of each test cases a single integer $n$ $(1 \leq n \leq {10}^{5})$ — 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 $2 \cdot {10}^{5}$.

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$,其中如果$s_i = s_{i-1}$,则$a_i = 0$;否则如果$s_i \ne s_{i-1}$,则$a_i = 1$。其中$a_1 = s_1$。这就是一个异或的差分数组。

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

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

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

  补充,这题是怎么想到用差分的呢。实际上,当区间$[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$我们进行去重操作,比如有${\color{Red}{11}}{\color{Blue}{0}}{\color{Red}{1}}{\color{Blue}{000}}{\color{Red}{1}}{\color{Blue}{0}}{\color{Red}{11}}$,那么去重后就是$1010101$,即把连续相同的一段都用这一段中的首个数字来表示,统一把操作归到首个数字。

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

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

  对于从$1$开始的序列,先考虑置换第$1$个位置,那么就会得到从$0$开始的$01$交替序列,接着从第$3$个位置开始置换后面每一个位置,因此这种方式的最优解为$1 + n - 2 = n - 1$。如果不置换第$1$个位置,那意味着整个序列要均为$1$,那么从第$2$个位置开始每个位置都要置换,因此答案就是$n - 1$。可以发现这两种方式得到的最优解是一样的,那么我们规定只要遇到$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 @ 2022-10-24 15:32  onlyblues  阅读(102)  评论(0编辑  收藏  举报
Web Analytics