题目描述

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

image-20260929105158223

image-20260929105158363

题意分析

每次可以把一个 0 翻为 1,或把一个 1 翻为 0。目标字符串必须是若干个 0 后接若干个 1,其中任意一段都可以为空,所以全零、全一也合法;要求的是最少翻转次数。

解法:DP(结尾为 0/1 的最少翻转)

核心思路

[!blue]

扫描到某个位置后,用 dp0 表示把当前前缀变成合法且末位为 0 的最小代价,用 dp1 表示变成合法且末位为 1 的最小代价。合法字符串一旦出现 1 就不能再出现 0,所以 dp0 对应的前缀实际上必须全为零。

设当前原字符为 c。如果把它作为末位 0,前面的前缀也只能处于 dp0,新代价为 旧dp0 + (c=='1' ? 1 : 0)。如果把它作为末位 1,前面既可以全零,也可以已经以一结尾,因此新代价为 min(旧dp0,旧dp1) + (c=='0' ? 1 : 0)。

这两种转移列出了合法末位的全部前驱,没有允许 1 后接 0;对每种末位选择最便宜的前驱,就得到该状态的最小代价。依次处理所有字符后,两种状态覆盖了全部合法终态,答案取它们的较小值。

两个初值都设为零,是给首字符提供同一个空前缀的零代价,并不表示空串真的有某个末位。每轮计算必须使用旧状态:先用临时变量保存新 dp1,再更新 dp0,最后写回 dp1,避免把当前字符重复计入前驱代价。

解题步骤

  1. 空前缀的两个基础代价均设为零。
  2. 新 dp1 取旧两状态较小值,并在当前为零时加一。
  3. dp0 在当前为一时加一。
  4. 保存新 dp1,最终返回两状态较小值。

已经单调、全部为零或全部为一的字符串,都能沿着无需翻转的转移得到零代价。不能只返回 dp1,否则会强迫合法的全零结果出现一个 1。

代码实现

class Solution {
    public int minFlipsMonoIncr(String s) {
        int dp0 = 0;
        int dp1 = 0;

        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            // 一结尾可接两种旧状态,先保存结果以免旧的零结尾状态被覆盖。
            int newDp1 = Math.min(dp0, dp1) + (c == '0' ? 1 : 0);

            dp0 = dp0 + (c == '1' ? 1 : 0);
            dp1 = newDp1;
        }

        return Math.min(dp0, dp1);
    }
}
func minFlipsMonoIncr(s string) int {
    dp0, dp1 := 0, 0
    for i := 0; i < len(s); i++ {
        c := s[i]
        // 一结尾可接两种旧状态,先保存结果以免旧的零结尾状态被覆盖。
        newDp1 := min(dp0, dp1)
        if c == '0' {
            newDp1++
        }
        if c == '1' {
            dp0++
        }
        dp1 = newDp1
    }
    return min(dp0, dp1)
}

func min(a, b int) int {
    if a < b {
        return a
    }
    return b
}

复杂度分析

  • 时间复杂度:$O(n)$,每个字符只更新两个状态。
  • 空间复杂度:$O(1)$,两个状态与临时值。

关键点总结

[!green]

  • 零结尾只能来自零结尾,一结尾可以来自两种状态。
  • 单调允许重复零或一,不是严格递增。
  • 全零也是合法终态,不能只返回 dp1。

易错点总结

[!yellow]

  • dp0 不能从 dp1 转移,否则会接受一后面再次出现零。
  • 先更新 dp0 再计算新 dp1,可能重复计算当前字符的代价。
  • 翻转有两个方向,不能预先限定只把零变一或只把一变零。

相似题目

题目 难度 关联与区别
1653. 使字符串平衡的最少删除次数 中等 同样让两类字符按先后顺序排列,原题通过删除修正,本题通过翻转修正。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2022/90470912
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!