Strings · Medium

56. Split a Binary String Into Maximum Number of Balanced Substrings

Topics: String, Greedy

Solve it on DeVert Campus - free code runner

Problem

Given a binary string s, split it into the maximum possible number of contiguous, non-overlapping substrings such that each substring has an equal number of 0s and 1s (every character of s must belong to exactly one substring). Print that maximum count.

Input and output

Input: A single line containing the binary string s. Output: Print a single integer - the maximum number of balanced substrings s can be split into.

Example

Input:
0100110101
Output:
4

Constraints

1 <= |s| <= 10^5, total count of 0s in s equals total count of 1s

Key observation

The problem guarantees the total count of 0s equals the total count of 1s in the whole string - this guarantees the running balance ALWAYS returns to exactly 0 by the very end, so the greedy cutting process is guaranteed to use up the entire string cleanly.

Intuition

Split a binary string into as many pieces as possible, where every piece has an equal number of 0s and 1s. Brute force: Try every possible way of choosing cut points directly and check if each resulting piece is balanced - this branches combinatorially over all possible partition choices, impractical without a smarter approach. Optimised: A greedy approach works perfectly here: cutting the MOMENT the running balance returns to zero is always at least as good as waiting longer, since a later cut can only ever merge what could have been two valid pieces into one, never creating additional pieces - this greedy choice is provably optimal and needs only a single linear scan.

Optimal - Greedy Single Pass

Scan the string once while maintaining a running balance that increases by 1 for '1' and decreases by 1 for '0'; every time this balance returns to exactly 0, that means the substring since the last cut is balanced, so close it off as one segment and increment the answer counter. This works because cutting as early as possible (the moment balance hits zero) can never do worse than waiting longer, since any longer balanced substring could always have been split into the earlier balanced piece plus a remainder that is itself still balanced. Time: O(n) Space: O(1)

Better - DP with Prefix Counts

Precompute prefix counts of 0s and 1s so the balance of any substring [i, j] can be checked in O(1), then use DP where dp[i] is the max number of balanced pieces achievable in the first i characters, trying every possible end point j > i for the next piece and taking dp[i] = max(dp[j]) + 1 wherever [i, j] is balanced. This avoids re-scanning substrings from scratch but still tries every candidate split point for each starting position. Time: O(n^2) Space: O(n)

Approach 3

Recursively try every possible way to place the next split boundary after the current position, check whether the resulting substring is balanced (equal 0s and 1s), and recurse on the remainder to maximize the count of balanced pieces. This explores an exponential number of ways to cut the string even though most cuts are never balanced. Time: O(2^n) Space: O(n) recursion depth

Step by step

1. Scan the string left to right, keeping a running balance: add 1 for every '1' seen, subtract 1 for every '0'. 2. Every time this running balance returns exactly to 0, that marks the end of one valid, balanced piece - cut here and start counting the next piece fresh. 3. Count how many times this balance-returns-to-zero event happens across the whole string.

Complexity

Time: O(n) - one single pass through the string, tracking a running balance counter. Space: O(1) - just the running balance and a cut counter.

Edge cases

Since 0-count must equal 1-count, the smallest valid s has length 2 (e.g. "01", giving 1 split) - test that; also test a fully alternating string like "0101...01" (splits into |s|/2 pieces), a string only splittable as one whole piece (e.g. "0011"), and |s|=10^5 for performance.

Interview tip

Be ready to justify WHY the greedy 'cut immediately when balance hits zero' strategy is provably optimal - a common interview follow-up is proving that waiting for a later zero-crossing can never produce MORE pieces than cutting immediately.

All DSA problems