持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第16天,点击查看活动详情
题目
如果一个二进制字符串,是以一些 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 <= 10^5s[i]为'0'或'1'
思考
本题难度中等。
首先是读懂题意。对于一个二进制字符串s,我们需要将任何0翻转为1或者将1翻转为0,使其满足单调递增的条件。最终我们返回使s单调递增的最小翻转次数。
我们可以使用前缀和的方法。我们利用前缀和可以快速地计算出当遍历到当前字符时,前面的1有多少个以及后面的0有多少个。我们先计算前缀和数组preSum。preSum[i] 表示索引i前面有几个数字1。假设最终答案是x个0和n-x个1,当x=3时,可知 preSum[3]=1,左边1个1,需要1次翻转。右边1的数量是preSum[n]-preSum[x]=2,0的数量是(n-x)-(preSum[n]-preSum[x])=1,需要1次翻转。
解答
方法一:前缀和
/**
* @param {string} s
* @return {number}
*/
var minFlipsMonoIncr = function(s) {
const n = s.length
let preSum = new Array(n + 1).fill(0) // 数组长度是n+1
for (let i = 0; i < n; i++) {
preSum[i+1] = preSum[i] + (s[i] === '1' ? 1 : 0)
}
let ans = Number.MAX_SAFE_INTEGER
for (let i = 0; i <= n; i++) {
ans = Math.min(ans, preSum[i] + n-i-(preSum[n]-preSum[i]))
}
return ans
}
// 执行用时:76 ms, 在所有 JavaScript 提交中击败了87.69%的用户
// 内存消耗:47.2 MB, 在所有 JavaScript 提交中击败了72.31%的用户
// 通过测试用例:93 / 93
复杂度分析:
- 时间复杂度:O(n),其中 n 为字符串s的长度。
- 空间复杂度:O(n)。