持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第17天,点击查看活动详情
题目描述
如果一个二进制字符串,是以一些 0(可能没有 0)后面跟着一些 1(也可能没有 1)的形式组成的,那么该字符串是 单调递增 的。
给你一个二进制字符串 s,你可以将任何 0 翻转为 1 或者将 1 翻转为 0 。
返回使 s 单调递增的最小翻转次数。
示例 1:
输入:s = "00110"
输出:1
解释:翻转最后一位得到 00111.
示例 2:
输入:s = "010110"
输出:2
解释:翻转得到 011111,或者是 000111。
示例 3:
输入:s = "00011000"
输出:2
解释:翻转得到 00000000。
提示:
- 1 <= s.length <= 105
- s[i] 为 '0' 或 '1'
思路
本题可以从结果出发,最后满足题意的字符串必然是这样一种形式 0...01...1,这里0的个数和1的个数都可以为0,即可以全部是1或者全部是0。我们可以考虑这里的第1个1的位置,通过枚举这个位置可能的情况,计算出每种情况需要翻转的数量,然后求最小值即可。
假设枚举到位置k,那么需要翻转的数量 = k之前(不含)1的数量 + k之后(包含)0的数量。每次枚举k的值,我们都重新计算这2部分的值很浪费,有很多重复计算,所以我们可以定义2个数组preOne和postZero来分别表示k之前(不含)1的数量 和 k之后(包含)0的数量。我们可以从左到右遍历来计算preOne的值,从右到左遍历来计算postZero的值。
有一个注意点是,第1个1可能在数组不存在的len位置,即数组存在全部是0的情况,所以遍历i时候要从0遍历到len,而不是len-1。
Java版本代码
class Solution {
public int minFlipsMonoIncr(String s) {
char[] cs = s.toCharArray();
int len = cs.length;
// i位置(不包含)之前1的数量
int[] preOne = new int[len+1];
// 遍历cs,计算preOne的值
for (int i = 1; i <= len; i++) {
if (cs[i-1] == '1') {
preOne[i] = preOne[i-1] + 1;
} else {
preOne[i] = preOne[i-1];
}
}
// i位置(包含)之后0的数量
int[] postZero = new int[len+1];
// 反向遍历cs,计算postZero的值
for (int j = len-1; j >= 0; j--) {
if (cs[j] == '0') {
postZero[j] = postZero[j+1] + 1;
} else {
postZero[j] = postZero[j+1];
}
}
// ans初始化一个大值,这里不需要使用Integer.MAX_VALUE,因为字符串长度为len,所以ans最大值就是len
int ans = len;
for (int i = 0; i <= len; i++) {
ans = Integer.min(ans, preOne[i] + postZero[i]);
}
return ans;
}
}