LeetCode:926. 将字符串翻转到单调递增

94 阅读1分钟

本文已参与「新人创作礼」活动, 一起开启掘金创作之路。

926. 将字符串翻转到单调递增

来源:力扣(LeetCode)

链接: leetcode.cn/problems/fl…

如果一个二进制字符串,是以一些 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 <= 10510^5

  • s[i] 为 '0' 或 '1'

解法

  • 动态规划:这种类型的题目有前后关系顺序,且只有两种状态(元素为0或1),整个题目可以转化为前i个元素 由于字符串 s 的每个位置的字符可以是 0 或 1,因此对于每个位置需要分别计算该位置的字符是 0 和该位置的字符是 1 的情况下的最小翻转次数。

假设字符串 s 的长度是 n,对于 0i<n0 \le i < n,用 dp[i][0]\textit{dp}[i][0]dp[i][1]\textit{dp}[i][1] 分别表示下标 i处的字符为 01的情况下使得 s[0..i]s[0 .. i] 单调递增的最小翻转次数。

i=0i = 0 时,对应长度为 1 的前缀,一定满足单调递增,因此 dp[0]\textit{dp}[0]dp[0][1]\textit{dp}[0][1] 的值取决于字符 s[i]s[i]。具体而言,dp[0][0]=I(s[0]=‘1’)\textit{dp}[0][0] = \mathbb{I}(s[0] = \text{`1'})dp[0][1]=I(s[0]=‘0’)\textit{dp}[0][1] = \mathbb{I}(s[0] = \text{`0'}),其中 I\mathbb{I} 为示性函数,当事件成立时示性函数值为 1,当事件不成立时示性函数值为 0。

当 1i<n \le i < n 时,考虑下标 i 处的字符。如果下标 i 处的字符是 0,则只有当下标 i - 1处的字符是 0 时才符合单调递增;如果下标 i 处的字符是 1,则下标 i−1 处的字符是 0 或 1 都符合单调递增,此时为了将翻转次数最小化,应分别考虑下标 i−1 处的字符是 0 和 1 的情况下需要的翻转次数,取两者的最小值。

在计算 dp[i][0]\textit{dp}[i][0]dp[i][1]\textit{dp}[i][1] 时,还需要根据 s[i]s[i]的值决定下标 i 处的字符是否需要翻转,因此可以得到如下状态转移方程,其中 I\mathbb{I}为示性函数:

dp[i][0]=dp[i1][0]+I(s[i]=‘1’)dp[i][1]=min(dp[i1][0],dp[i1][1])+I(s[i]=‘0’)\begin{aligned} \textit{dp}[i][0] &= \textit{dp}[i - 1][0] + \mathbb{I}(s[i] = \text{`1'}) \\ \textit{dp}[i][1] &= \min(\textit{dp}[i - 1][0], \textit{dp}[i - 1][1]) + \mathbb{I}(s[i] = \text{`0'}) \end{aligned}

遍历字符串 s计算每个下标处的状态值,遍历结束之后,dp[n1][0]\textit{dp}[n - 1][0]dp[n1][1]\textit{dp}[n - 1][1] 中的最小值即为使字符串 s 单调递增的最小翻转次数。

实现方面有以下两点可以优化。

  • 可以将边界情况定义为 dp[1][0]=dp[1][1]=0\textit{dp}[-1][0] = \textit{dp}[-1][1] = 0 ,则可以从下标 0 开始使用状态转移方程计算状态值。

  • 由于 dp[i]\textit{dp}[i] 的值只和 dp[i1]\textit{dp}[i - 1] 有关,因此在计算状态值的过程中只需要维护前一个下标处的状态值,将空间复杂度降低到 O(1)。

代码实现

动态规划

python实现

class Solution:
    def minFlipsMonoIncr(self, s: str) -> int:
        n = len(s)
        if n <= 1:
            return 0
        
        dp0, dp1 = 0, 0
        for c in s:
            dp0New, dp1New = dp0, min(dp0, dp1)
            if c == '1':
                dp0New += 1
            else:
                dp1New += 1
            dp0, dp1 = dp0New, dp1New
        return min(dp0, dp1)

c++实现

class Solution {
public:
    int minFlipsMonoIncr(string s) {
        int n = s.size();
        if (n == 1)
            return 0;
        
        int dp0 = 0, dp1 = 0;
        for (auto c: s)
        {
            int dp0New = dp0, dp1New = min(dp0, dp1);
            if (c == '1')
                dp0New++;
            else
                dp1New++;
            dp0 = dp0New, dp1 = dp1New;
        }
        return dp0 <= dp1 ? dp0 : dp1;
    }
};

复杂度分析

  • 时间复杂度: O(n)O(n)
  • 空间复杂度: O(1)O(1)