leetcode-将字符串翻转到单调递增

118 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第17天,点击查看活动详情

题目描述

如果一个二进制字符串,是以一些 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 <= 105
  • s[i] 为 '0' 或 '1'

思路

本题可以从结果出发,最后满足题意的字符串必然是这样一种形式 0...01...1,这里0的个数和1的个数都可以为0,即可以全部是1或者全部是0。我们可以考虑这里的第1个1的位置,通过枚举这个位置可能的情况,计算出每种情况需要翻转的数量,然后求最小值即可。
假设枚举到位置k,那么需要翻转的数量 = k之前(不含)1的数量 + k之后(包含)0的数量。每次枚举k的值,我们都重新计算这2部分的值很浪费,有很多重复计算,所以我们可以定义2个数组preOne和postZero来分别表示k之前(不含)1的数量 和 k之后(包含)0的数量。我们可以从左到右遍历来计算preOne的值,从右到左遍历来计算postZero的值。
有一个注意点是,第1个1可能在数组不存在的len位置,即数组存在全部是0的情况,所以遍历i时候要从0遍历到len,而不是len-1。

Java版本代码

class Solution {
    public int minFlipsMonoIncr(String s) {
        char[] cs = s.toCharArray();
        int len = cs.length;
        // i位置(不包含)之前1的数量
        int[] preOne = new int[len+1];
        // 遍历cs,计算preOne的值
        for (int i = 1; i <= len; i++) {
            if (cs[i-1] == '1') {
                preOne[i] = preOne[i-1] + 1;
            } else {
                preOne[i] = preOne[i-1];
            }
        }
        // i位置(包含)之后0的数量
        int[] postZero = new int[len+1];
        // 反向遍历cs,计算postZero的值
        for (int j = len-1; j >= 0; j--) {
            if (cs[j] == '0') {
                postZero[j] = postZero[j+1] + 1;
            } else {
                postZero[j] = postZero[j+1];
            }
        }
        // ans初始化一个大值,这里不需要使用Integer.MAX_VALUE,因为字符串长度为len,所以ans最大值就是len
        int ans = len;
        for (int i = 0; i <= len; i++) {
            ans = Integer.min(ans, preOne[i] + postZero[i]);
        }
        return ans;
    }
}