将字符串翻转到单调递增
如果一个二进制字符串,是以一些 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 <= 105s[i]为'0'或'1'
解题
由题可知最终输出的字符串会有以下3中类型:0000,1111,0011
正序遍历s:
定义2个变量 prevZeroCount,prevOneCount分别表示当前字符之前已有的0,1的个数,同时定义一个字典存储已遍历部分分别翻转为0000,1111,0011所需的次数 dic:{zero:0,one:0,zeroOne:0}
-
当遇到
0时prevZeroCount += 1,此时已遍历部分是0000类型 -
当遇到
1时如果prevZeroCount === 0则,prevOneCount += 1,此时已遍历部分是1111类型 -
否则需要计算已遍历部分需要翻转,计算需要反转次数
- 转为成
0000型:前序为0的+prevOneCount,(将待翻转的1全部翻转为0) ,即dic.zero + prevOneCount - 转为成
1111型:前序为0或1的最小值+prevZeroCount,(将待翻转的0全部翻转为1),即min(dic.zero + dic.one) + prevZeroCount - 转为成
0011型: 前序为0或1或01的最小值+prevZeroCount,(将待翻转的0全部翻转为1),即min(dic.zero + dic.one + dic.zeroOne) + prevZeroCount
同时将
prevZeroCount重置为0,prevOneCount重置为1(当前字符为1) - 转为成
遍历结束后取min(dic.zero + dic.one + dic.zeroOne)为最小翻转次数
代码实现:
var minFlipsMonoIncr = function (s) {
let prevZeroCount = 0,
prevOneCount = 0,
dic = { zero: 0, one: 0, zeroOne: 0 },
start = 0,
end = s.length - 1
while (s[start] == '0' || s[end] == '1') { // 排除左边的0和右边的1最终会剩下101010这样的一组数据
if (s[start] == '0') {
start += 1
}
if (s[end] == '1') {
end -= 1
}
}
for (var i = start; i <= end; i++) {
if (s[i] === '0') {
prevZeroCount += 1
} else {
if (prevZeroCount === 0) {
prevOneCount += 1
} else {
var tmpZero = dic.zero + prevOneCount
var tmpOne = Math.min(dic.one, dic.zeroOne) + prevZeroCount
var tmoZeroOne = Math.min(dic.zero, dic.one, dic.zeroOne) + prevZeroCount
dic.zero = tmpZero
dic.one = tmpOne
dic.zeroOne = tmoZeroOne
prevZeroCount = 0
prevOneCount = 1
}
}
}
dic.zero += prevOneCount
dic.one = Math.min(dic.one, dic.zeroOne) + prevZeroCount
dic.zeroOne = Math.min(dic.zero, dic.one, dic.zeroOne) + prevZeroCount
return Math.min(dic.zero, dic.one, dic.zeroOne)
}
时间复杂度O(n)