本文已参与「新人创作礼」活动, 一起开启掘金创作之路。
926. 将字符串翻转到单调递增
来源:力扣(LeetCode)
如果一个二进制字符串,是以一些 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 <=
-
s[i] 为 '0' 或 '1'
解法
- 动态规划:这种类型的题目有前后关系顺序,且只有两种状态(元素为0或1),整个题目可以转化为前i个元素 由于字符串 s 的每个位置的字符可以是 0 或 1,因此对于每个位置需要分别计算该位置的字符是 0 和该位置的字符是 1 的情况下的最小翻转次数。
假设字符串 s 的长度是 n,对于 ,用 和 分别表示下标 i处的字符为 0和 1的情况下使得 单调递增的最小翻转次数。
当 时,对应长度为 1 的前缀,一定满足单调递增,因此 和 的值取决于字符 。具体而言,,,其中 为示性函数,当事件成立时示性函数值为 1,当事件不成立时示性函数值为 0。
当 1 时,考虑下标 i 处的字符。如果下标 i 处的字符是 0,则只有当下标 i - 1处的字符是 0 时才符合单调递增;如果下标 i 处的字符是 1,则下标 i−1 处的字符是 0 或 1 都符合单调递增,此时为了将翻转次数最小化,应分别考虑下标 i−1 处的字符是 0 和 1 的情况下需要的翻转次数,取两者的最小值。
在计算 和 时,还需要根据 的值决定下标 i 处的字符是否需要翻转,因此可以得到如下状态转移方程,其中 为示性函数:
遍历字符串 s计算每个下标处的状态值,遍历结束之后, 和 中的最小值即为使字符串 s 单调递增的最小翻转次数。
实现方面有以下两点可以优化。
-
可以将边界情况定义为 ,则可以从下标 0 开始使用状态转移方程计算状态值。
-
由于 的值只和 有关,因此在计算状态值的过程中只需要维护前一个下标处的状态值,将空间复杂度降低到 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;
}
};
复杂度分析
- 时间复杂度:
- 空间复杂度: