leetcode
leetcode 801 ~ 850
将字符串翻转到单调递增

将字符串翻转到单调递增

难度:

标签:

题目描述

代码结果

运行时间: 64 ms, 内存: 16.5 MB


/*
题目思路:
1. 我们需要找到最少的翻转次数,使得给定的二进制字符串单调递增。
2. 使用Java Stream对每一个位置计算前面的1变0和后面的0变1的最小次数。
3. 使用两个累加器leftFlip和rightFlip,分别计算前面和后面的翻转次数。
*/
import java.util.stream.IntStream;

public class Solution {
    public int minFlipsMonoIncr(String s) {
        int n = s.length();
        int[] leftFlip = new int[n + 1];
        int[] rightFlip = new int[n + 1];

        IntStream.range(0, n).forEach(i -> {
            leftFlip[i + 1] = leftFlip[i] + (s.charAt(i) == '0' ? 0 : 1);
        });

        IntStream.range(0, n).map(i -> n - 1 - i).forEach(i -> {
            rightFlip[i] = rightFlip[i + 1] + (s.charAt(i) == '1' ? 0 : 1);
        });

        return IntStream.range(0, n + 1)
                .map(i -> leftFlip[i] + rightFlip[i])
                .min()
                .orElse(Integer.MAX_VALUE);
    }
}

解释

方法:

此题解采用一种贪心策略,通过计数器来监视已遍历的 '1' 的数量。对于每个 '0',如果在此之前有 '1' 出现(即 count > 0),则将这个 '0' 翻转为 '1' 可以使得字符串趋向单调递增,同时将计数器减一表示减少一个需要翻转的 '1',同时累加翻转次数。如果遇到 '1',则增加计数器。这种方法实际上是在模拟将一部分 '1' 向前移动到所有的 '0' 之前,每次遇到 '0' 时,如果前面有 '1',就考虑将前面的 '1' 翻转成 '0',或者将当前的 '0' 翻转成 '1',选择翻转 '0' 是因为这可以帮助维持后续的单调递增状态。

时间复杂度:

O(n)

空间复杂度:

O(1)

代码细节讲解

🦆
为什么在遇到'0'时,选择翻转前面的'1'而不是直接翻转当前的'0'?
在处理字符串以使其单调递增时,直接翻转当前的'0'为'1'会简单地解决当前位置的问题,但不会减少后续可能需要的翻转次数。相反,通过翻转前面的'1'为'0',我们实际上是在减少后续操作中可能需要的翻转次数,因为这样可以有效减少已经计数的'1'的数量,从而在后续遇到的'0'前有更少的'1'需要处理。这种方法更符合贪心策略的目标,即尽可能减少整体的翻转次数。
🦆
在这种贪心策略中,维护的'1'的计数count是否可以解释为当前需要翻转的'1'的最小数量?
是的,维护的'1'的计数count实际上可以视为在当前点之前遇到的'1'的数量,这同样表示如果我们决定将所有后续的'0'都翻转为'1',那么这些'1'就是当前需要翻转的最小数量。这个计数帮助我们决定在遇到'0'时是否需要翻转以及翻转哪些数字,以达到最小化总翻转次数的目的。
🦆
如果输入的字符串已是单调递增,算法的输出是否为0?如果不是,应如何修改代码以处理这种特殊情况?
如果输入的字符串已是单调递增,算法的输出应该是0。在题解的算法中,当字符串已经是单调递增时,'1'的计数器将逐步增加,但不会有'0'出现在'1'之后,因此不会有翻转操作。所以,算法在这种情况下自然会输出0,无需额外修改。
🦆
算法在遇到连续的'0'时是否会重复计算翻转次数?例如在字符串'00011000'中处理后三个'0'的逻辑是什么?
在遇到连续的'0'时,算法不会重复计算翻转次数。以'00011000'为例,前三个'0'不会引发任何翻转操作,因为它们出现在任何'1'之前。当遇到'1'时,计数器开始增加。在处理最后三个'0'时,由于前面已记录两个'1'('11'),每遇到一个'0',都会将一个'1'的计数减去,并增加一个翻转次数。这样,后三个'0'会相应地减少两个'1'的计数,总共增加两次翻转操作。

相关问题