Leetcode 926:将字符串翻转到单调递增(最详细的解法!!!)

如果一个由 '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. 1 <= S.length <= 20000
  2. S 中只包含字符 '0''1'

解题思路

一个简单的想法是我们先同级S中的'0''1'的个数,可以通过collections.Counter统计,记为S_dict

0  0  1  1  0
      i

然后统计[0:i]这个区间内'0''1'的个数zerosones。最后我们只要将[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)这个区间内的元素按照之前的最小值取。转移方程就是

  • f ( n ) = m i n ( m , f ( n ) + 1 − i n t ( s ) )     m = s u m ( i n t ( S [ 0 : i + 1 ] ) ) f(n)=min(m,f(n)+1-int(s)) \ \ \ m=sum(int(S[0:i+1])) f(n)=min(m,f(n)+1int(s))   m=sum(int(S[0:i+1]))
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

如有问题,希望大家指出!!!

你可能感兴趣的:(Problems,leetcode解题指南)