如果一个由 '0'
和 '1'
组成的字符串,是以一些 '0'
(可能没有 '0'
)后面跟着一些 '1'
(也可能没有 '1'
)的形式组成的,那么该字符串是单调递增的。
我们给出一个由字符 '0'
和 '1'
组成的字符串 S
,我们可以将任何 '0'
翻转为 '1'
或者将 '1'
翻转为 '0'
。
返回使 S
单调递增的最小翻转次数。
示例 1:
输入:"00110"
输出:1
解释:我们翻转最后一位得到 00111.
示例 2:
输入:"010110"
输出:2
解释:我们翻转得到 011111,或者是 000111。
示例 3:
输入:"00011000"
输出:2
解释:我们翻转得到 00000000。
提示:
1 <= S.length <= 20000
S
中只包含字符 '0'
和 '1'
解题思路
一个简单的想法是我们先同级S
中的'0'
和'1'
的个数,可以通过collections.Counter
统计,记为S_dict
0 0 1 1 0
i
然后统计[0:i]
这个区间内'0'
和'1'
的个数zeros
和ones
。最后我们只要将[0:i]
都变成0
,将[i+1:]
都变成1
即可,计算变化的次数,取其中的最小值,也就是min(ones + S_dict['0']-zeros)
。
from collections import Counter
class Solution:
def minFlipsMonoIncr(self, S):
"""
:type S: str
:rtype: int
"""
zeros, ones, result = 0, 0, float('inf')
S_dict = Counter(S)
result = min(result, ones + S_dict['0']-zeros)
for val in S:
if val == '1':
ones += 1
else:
zeros += 1
result = min(result, ones + S_dict['0']-zeros)
return result
这个问题我们也可以通过动态规划来做,而且更加巧妙。我们遍历S
,对于当前遍历到的第i
个元素s
来说,我们需要的当前最小值来自于两种途径,一种是将[0,i]
这个区间内的元素全部变成0
,另一种是将第i
个元素变成1
,而[0,i)
这个区间内的元素按照之前的最小值取。转移方程就是
class Solution:
def minFlipsMonoIncr(self, S):
"""
:type S: str
:rtype: int
"""
m, n = 0, 0
for s in S:
m += int(s)
n = min(m, n + 1 - int(s))
return n
reference:
https://leetcode.com/problems/flip-string-to-monotone-increasing/discuss/183851/C++Java-4-lines-O(n)-or-O(1)-DP
我将该问题的其他语言版本添加到了我的GitHub Leetcode
如有问题,希望大家指出!!!