Leetcode 926. Flip String to Monotone Increasing

Problem Explanation

The task is to find the minimum number of flips required to make a binary (0, 1) string into a monotonically increasing string. A string is monotonically increasing if it contains a sequence of zeros, followed by a sequence of ones. In other words, once a '1' appears, no more '0's appear in the rest of the string. We can flip any '1' to '0' or '0' to '1'.

Let's walk through an example to understand this better. Suppose we have a string, "010110".

One possible way to make this string monotone increasing is by flipping the second and third characters from '1' to '0', then we get "000110". Another way is to flip the first and fourth characters from '0' to '1', then we get "110111". Both ways require 2 flips, so the minimum number of flips needed is 2.

Approach Explanation

This problem can be solved efficiently using dynamic programming. The main idea is to keep track of two states while traversing through the string, namely: the number of flips required to make the string up to index 'i' monotonically increasing with an ending '0' and the number of flips for an ending '1'.

Let dp[0] and dp[1] be the minimum number of flips to make the string up to index 'i' monotonically increasing with an ending '0' and '1'.

If we encounter a '0', we have to flip it to make the string end in '1', so dp[1] = min(dp[0], dp[1]) + 1. But if we want the string to end in '0', we don't have to do anything.

If we encounter a '1', we don't have to flip it to make the string end in '1', so dp[1] = min(dp[0], dp[1]). But if we want the string to end in '0', we need to flip the '1', so dp[0] = dp[0] + 1.

At the end, we return min(dp[0], dp[1]) as we want the minimum number of flips.

Java Solution

1
2java
3class Solution {
4    public int minFlipsMonoIncr(String s) {
5        int[] dp = new int[2];
6
7        for (char c : s.toCharArray()) {
8            int temp = dp[0] + (c == '1' ? 1 : 0);
9            dp[1] = Math.min(dp[0], dp[1]) + (c == '0' ? 1 : 0);
10            dp[0] = temp;
11        }
12
13        return Math.min(dp[0], dp[1]);
14    }
15}

Python3 Solution

1
2python
3class Solution:
4    def minFlipsMonoIncr(self, s: str) -> int:
5        dp = [0, 0]
6
7        for c in s:
8            temp = dp[0] + (c == '1')
9            dp[1] = min(dp[0], dp[1]) + (c == '0')
10            dp[0] = temp
11
12        return min(dp[0], dp[1])

JavaScript Solution

1
2javascript
3class Solution {
4  minFlipsMonoIncr(s) {
5    let dp = [0, 0];
6
7    for (let c of s) {
8      let temp = dp[0] + (c === '1' ? 1 : 0);
9      dp[1] = Math.min(dp[0], dp[1]) + (c === '0' ? 1 : 0);
10      dp[0] = temp;
11    }
12
13    return Math.min(dp[0], dp[1]);
14  }
15}

C++ Solution

1
2cpp
3class Solution {
4 public:
5  int minFlipsMonoIncr(string s) {
6    vector<int> dp(2);
7
8    for (const char c : s) {
9      const int temp = dp[0] + (c == '1');
10      dp[1] = min(dp[0], dp[1]) + (c == '0');
11      dp[0] = temp;
12    }
13
14    return min(dp[0], dp[1]);
15  }
16};

C# Solution

1
2csharp
3public class Solution {
4    public int MinFlipsMonoIncr(string s) {
5        int[] dp = new int[2];
6
7        foreach (char c in s) {
8            int temp = dp[0] + (c == '1' ? 1 : 0);
9            dp[1] = Math.Min(dp[0], dp[1]) + (c == '0' ? 1 : 0);
10            dp[0] = temp;
11        }
12
13        return Math.Min(dp[0], dp[1]);
14    }
15}

Conclusion

This problem essentially comes down to an optimization problem that can be solved with the dynamic programming technique. The fact that we keep track of the minimum number of flips needed to make the string monotonic whether ending in '0' or '1' is crucial for the solution to be efficient. In the end, we use these stored states to make a decision, which is a characteristic feature of dynamic programming.

Next time you encounter a similar problem, try to identify if you can use dynamic programming by looking at the problem's overlapping subproblems and the requirement for an optimized solution. And remember, the solution lies in accurately building and using the state-based dp values.

The given solutions in Java, Python3, C++, C#, and JavaScript are pretty straightforward once the approach is clear. They keep track of two things up to every index in the binary string: the minimum number of operations required to make the subarray ending here increasing and ending with a 0 and the minimum number of operations required for ending with a 1.

Understanding and implementing dynamic programming solutions improves problem-solving skills and allows for efficient solving of complex problems that would otherwise be impractical with other methods.


Got a question? Ask the Teaching Assistant anything you don't understand.

Still not clear? Ask in the Forum,  Discord or Submit the part you don't understand to our editors.


TA 👨‍🏫