String · 12 of 43
Split the Binary string into two substring with equal 0s and 1s
Cut a row of 0s and 1s into as many pieces as you can, so that every piece has as many 0s as 1s.
- Simple way
- time O(n^2)
room O(1) - Clever way
- time O(n)
room O(1)
The problem, simply
You get a string made only of 0 and 1. Cut it into pieces that sit next to each other and use every character. Each piece must have the same number of 0s and 1s.
Give back the largest number of pieces you can make. If there is no way at all, give back -1.
A story to remember it
Picture it
0100110101 cut into 01, 0011, 01 and 01. Each piece has as many 0s as 1s.
- done
- answer
Examples
A normal case
- In
s = '0100110101'- Out
4
The pieces are 01, 0011, 01 and 01. The score hits zero four times.
A long middle piece
- In
s = '0111100010'- Out
3
The pieces are 01, 111000 and 10. The middle part cannot be cut smaller, because no shorter start of it is level.
One piece only
- In
s = '1100'- Out
1
The score goes -1, -2, -1, 0. It is only level at the very end, so the whole string is one piece.
Impossible
- In
s = '0000000000'- Out
-1
There are no 1s at all, so no piece can ever be level.
Uneven at the end
- In
s = '010'- Out
-1
01 is a fair piece, but the last 0 is left alone. Every character must be used, so the answer is -1.
Way 1: the simple way
Make the shortest fair piece you can, then start the next piece right after it. To check whether a piece is fair, count its 0s and 1s from scratch.
- Start a piece at
start. - Try
end = start, start + 1, .... For each one, count the 0s and 1s ins[start..end]. - The first
endwhere the counts are equal closes the piece. Add one to the answer and setstart = end + 1. - If no
endworks, the rest of the string can never be fair, so return-1.
Why the shortest piece? Cutting early never hurts. If a piece is fair, what comes after it still needs to be fair on its own, and cutting earlier gives you more pieces.
class Solution {
public int maxSplits(String s) {
int n = s.length(), pieces = 0, start = 0;
while (start < n) {
int end = -1;
for (int e = start; e < n && end == -1; e++) {
int zeros = 0, ones = 0;
for (int k = start; k <= e; k++) { // count from scratch
if (s.charAt(k) == '0') zeros++; else ones++;
}
if (zeros == ones) end = e; // shortest fair piece
}
if (end == -1) return -1; // the rest can never be fair
pieces++;
start = end + 1;
}
return pieces;
}
}How long it takes: for a piece of length m we recount up to m times, so up to m * m steps. All pieces together cost at most O(n^2). Extra room: two counters, O(1).
Way 2: the clever way
Do not recount. Keep the seesaw score as you walk: plus one for a 0, minus one for a 1. The score at any point is (0s so far) minus (1s so far).
- Walk the string once, updating
balance. - Every time
balancebecomes0, the part since the last cut is fair. Count a piece. - At the end, if
balanceis not0, the last part is unfair, so return-1.
A cut is only possible where the score is zero, and cutting at every zero gives fair pieces. So the number of zeros is the answer.
Cut each time the seesaw is level
01 / 10
Read 0. A red kid sits down, so the score is 1. The first piece is open.
- balance
- 1
- pieces
- 0
- looking now
- done
- answer
- path
class Solution {
public int maxSplits(String s) {
int balance = 0, pieces = 0;
for (int i = 0; i < s.length(); i++) {
balance += (s.charAt(i) == '0') ? 1 : -1; // red kid +1, blue kid -1
if (balance == 0) pieces++; // level: a fair piece ends here
}
return balance == 0 ? pieces : -1; // the last part must be fair too
}
}How long it takes: one walk, O(n). Extra room: two numbers, O(1).
Watch out for
- An empty string. There are no pieces, so return
0, or whatever the interviewer defines. Ask. - Only comparing the totals. Equal totals tell you the answer is at least
1, not how many pieces you can make. - Returning the list of pieces instead of the count. Remember the index of the last cut and use
s.substring(start, i + 1)at each zero.
What interviewers ask next
- Print the pieces too. Keep
start, and at every zero adds.substring(start, i + 1)and setstart = i + 1. - What if the balance can go below zero? That is fine here. It only matters in bracket problems, where a
)before its(is not allowed. - Longest part with equal 0s and 1s, not starting at the front (LeetCode 525). Store the first index of each balance in a map. When a balance repeats, the part between is level.
- Count all fair parts, not only the cuts (LeetCode 696 and similar). Same balance idea, but count how many earlier positions had the same balance.
- Is cutting at every zero always the maximum? Yes. Any valid cut must be at a zero, so you cannot have more cuts than zeros.
Remember this
Grown-up words: the seesaw score is a running balance (a prefix sum of plus and minus ones), cutting at the earliest level point is a greedy choice, and the level points are where the prefix sum equals zero.