Find the Longest Balanced Substring of a Binary String
EasyPrompt
You are given a binary string s consisting only of zeroes and ones.
A substring of s is considered balanced if all zeroes are before ones and the number of zeroes is equal to the number of ones inside the substring. Notice that the empty substring is considered a balanced substring.
Return the length of the longest balanced substring of s.
A substring is a contiguous sequence of characters within a string.
Example 1:
Input: s = "01000111"
Output: 6
Explanation: The longest balanced substring is "000111", which has length 6.Example 2:
Input: s = "00111"
Output: 4
Explanation: The longest balanced substring is "0011", which has length 4. Example 3:
Input: s = "111"
Output: 0
Explanation: There is no balanced substring except the empty substring, so the answer is 0.
Constraints:
1 <= s.length <= 50'0' <= s[i] <= '1'
Approaches
3 approaches with complexity analysis and trade-offs.
This approach exhaustively checks every single substring of the given string s. For each substring, it validates whether it is 'balanced' according to the problem's definition: an equal number of zeroes and ones, with all zeroes appearing before all ones.
Algorithm
- Initialize a variable
maxLengthto 0. - Generate all possible substrings of
susing nested loops. The outer loopiiterates from0ton-1(start index), and the inner loopjiterates fromiton-1(end index). - For each substring, create a helper function
isBalanced(sub)to check if it meets the criteria. - The
isBalancedfunction checks if the substring's length is even (say,2k), if its firstkcharacters are all '0's, and if its lastkcharacters are all '1's. - If a substring is balanced, update
maxLength = max(maxLength, length of substring). - After checking all substrings, return
maxLength.
Walkthrough
The brute-force method is the most straightforward way to solve the problem. We can use two nested loops to generate every possible contiguous substring. The outer loop selects the starting index i, and the inner loop selects the ending index j.
For each substring obtained, we pass it to a helper function. This function first checks if the length is even, say 2k. If not, it can't be balanced. If it is even, it then verifies that the first half of the substring consists entirely of k zeroes and the second half consists entirely of k ones.
We maintain a variable, maxLength, initialized to 0. Whenever we find a balanced substring, we compare its length with maxLength and update it if the new length is greater. After iterating through all possible substrings, maxLength will hold the length of the longest one found.
class Solution { public int findTheLongestBalancedSubstring(String s) { int n = s.length(); int maxLength = 0; for (int i = 0; i < n; i++) { for (int j = i; j < n; j++) { String sub = s.substring(i, j + 1); if (isBalanced(sub)) { maxLength = Math.max(maxLength, sub.length()); } } } return maxLength; } private boolean isBalanced(String sub) { int len = sub.length(); if (len == 0 || len % 2 != 0) { return false; } int k = len / 2; // Check first half for '0's for (int i = 0; i < k; i++) { if (sub.charAt(i) != '0') { return false; } } // Check second half for '1's for (int i = k; i < len; i++) { if (sub.charAt(i) != '1') { return false; } } return true; }}Complexity
Time
O(n^3), where n is the length of the string. There are O(n^2) possible substrings. For each substring, the `isBalanced` check takes O(n) time in the worst case. Thus, the total time complexity is O(n^2 * n) = O(n^3).
Space
O(n), where n is the length of the string. In Java, `s.substring()` can create a new string object, which in the worst case can have a length of n.
Trade-offs
Pros
Simple to conceptualize and implement.
Guaranteed to find the correct answer by checking all possibilities.
Cons
Highly inefficient due to its cubic time complexity.
Generates and checks many substrings that cannot possibly be balanced.
Likely to time out on larger constraints, though it will pass for this problem's constraints (n <= 50).
A more optimized approach is to realize that any valid balanced substring must contain a "01" pattern at its core. We can iterate through the string to find every occurrence of this "01" pattern and then expand outwards from this 'center' to find the longest possible balanced substring.
Algorithm
- Initialize
maxLengthto 0. - Iterate through the string
swith an indexifrom0ton-2. - At each
i, check ifs[i]is '0' ands[i+1]is '1'. This"01"pair is a potential center of a balanced substring. - If a center is found, use two pointers,
l = iandr = i + 1. - Expand the left pointer
ltowards the beginning of the string as long as the characters are '0's, counting them. - Expand the right pointer
rtowards the end of the string as long as the characters are '1's, counting them. - Calculate the number of consecutive zeroes (
zeros) to the left and ones (ones) to the right. - The length of the balanced substring for this center is
2 * min(zeros, ones). - Update
maxLength = max(maxLength, 2 * min(zeros, ones)). - Return
maxLengthafter the loop finishes.
Walkthrough
Instead of checking every substring, we can focus on the essential structure of a balanced substring: a block of zeroes followed immediately by a block of ones. This structure implies there must be a "01" transition point.
We can iterate through the string and look for every index i where s.charAt(i) == '0' and s.charAt(i+1) == '1'. Each such pair is a potential center of a balanced substring.
Once we find such a center at (i, i+1), we use two pointers to expand outwards. A left pointer l starts at i and moves left, counting consecutive '0's. A right pointer r starts at i+1 and moves right, counting consecutive '1's. After both pointers stop, we have a count of zeroes and ones. The length of the balanced substring we can form is twice the minimum of these two counts. We update a global maxLength with this value if it's larger than the current maximum.
class Solution { public int findTheLongestBalancedSubstring(String s) { int maxLength = 0; int n = s.length(); for (int i = 0; i < n - 1; i++) { // Find the "01" center if (s.charAt(i) == '0' && s.charAt(i + 1) == '1') { int zeros = 0; int ones = 0; // Expand left for '0's from i int l = i; while (l >= 0 && s.charAt(l) == '0') { zeros++; l--; } // Expand right for '1's from i+1 int r = i + 1; while (r < n && s.charAt(r) == '1') { ones++; r++; } maxLength = Math.max(maxLength, 2 * Math.min(zeros, ones)); } } return maxLength; }}Complexity
Time
O(n^2), where n is the length of the string. The main loop runs `n` times. In the worst-case scenario (like a string of all '0's followed by all '1's), the inner expansion loops can scan a large portion of the string for each potential center, leading to quadratic complexity.
Space
O(1), as it only uses a constant number of variables for pointers and counters.
Trade-offs
Pros
Significantly more efficient than the O(n^3) brute-force approach.
Reduces unnecessary checks by focusing only on promising candidates.
Cons
Not the most optimal solution.
It re-scans parts of the string multiple times during the expansion phase for different centers.
The most efficient solution involves a single pass through the string. By maintaining counts of the current consecutive block of zeroes and the subsequent consecutive block of ones, we can calculate the length of the longest balanced substring in linear time.
Algorithm
- Initialize
maxLength = 0,zeros = 0, andones = 0. - Iterate through the string
scharacter by character from left to right. - If the current character is '0':
- Check if the previous character was a '1' (which we can infer if
ones > 0). If so, it means a...10...pattern has occurred, breaking the current0...1...group. Resetzerosandonesto 0 to start a new group. - Increment
zeros.
- Check if the previous character was a '1' (which we can infer if
- If the current character is '1':
- Increment
ones.
- Increment
- After processing each character, we have the count of the current consecutive '1's block (
ones) and the '0's block that came immediately before it (zeros). - Calculate a potential balanced substring length as
2 * min(zeros, ones). - Update
maxLength = max(maxLength, 2 * min(zeros, ones)). - After the loop, return
maxLength.
Walkthrough
This approach optimizes the process down to a single scan. We use two counters, zeros and ones, to keep track of the lengths of the current consecutive blocks of '0's and '1's.
We iterate through the string. When we see a '0', we increment the zeros counter. However, if the '0' follows a block of '1's (i.e., ones > 0), it means the previous 0...1... group has ended. We must reset our counts (zeros = 1, ones = 0) to start counting a new potential group. When we see a '1', we simply increment the ones counter.
At every step, after updating the counters, we have a potential balanced substring formed by the preceding zeros and the current ones. The length of this substring is 2 * min(zeros, ones). We continuously update a maxLength variable with the maximum length found so far. This way, we find the result by examining each character only once.
class Solution { public int findTheLongestBalancedSubstring(String s) { int maxLength = 0; int zeros = 0; int ones = 0; for (int i = 0; i < s.length(); i++) { if (s.charAt(i) == '0') { // If we encounter a '0' after a sequence of '1's, // the previous group is finished. Start a new one. if (ones > 0) { zeros = 0; ones = 0; } zeros++; } else { // s.charAt(i) == '1' ones++; } // A balanced substring can be formed with the current number of ones // and the preceding number of zeros. maxLength = Math.max(maxLength, 2 * Math.min(zeros, ones)); } return maxLength; }}Complexity
Time
O(n), where n is the length of the string. This is because we iterate through the string exactly once.
Space
O(1), as we only use a few constant-space variables to store the counts and the maximum length.
Trade-offs
Pros
Optimal time complexity of O(n).
Optimal space complexity of O(1).
Highly efficient and scalable.
Cons
The logic for resetting the counters can be slightly less intuitive to grasp initially compared to more direct methods.
Solutions
Solution
class Solution {public int findTheLongestBalancedSubstring(String s) { int n = s.length(); int ans = 0; for (int i = 0; i < n; ++i) { for (int j = i + 1; j < n; ++j) { if (check(s, i, j)) { ans = Math.max(ans, j - i + 1); } } } return ans; }private boolean check(String s, int i, int j) { int cnt = 0; for (int k = i; k <= j; ++k) { if (s.charAt(k) == '1') { ++cnt; } else if (cnt > 0) { return false; } } return cnt * 2 == j - i + 1; }}class Solution {public: int findTheLongestBalancedSubstring(string s) { int n = s.size(); int ans = 0; auto check = [&](int i, int j) -> bool { int cnt = 0; for (int k = i; k <= j; ++k) { if (s[k] == '1') { ++cnt; } else if (cnt) { return false; } } return cnt * 2 == j - i + 1; }; for (int i = 0; i < n; ++i) { for (int j = i + 1; j < n; ++j) { if (check(i, j)) { ans = max(ans, j - i + 1); } } } return ans; }};class Solution : def findTheLongestBalancedSubstring ( self , s : str ) -> int : def check ( i , j ): cnt = 0 for k in range ( i , j + 1 ): if s [ k ] == '1' : cnt += 1 elif cnt : return False return cnt * 2 == ( j - i + 1 ) n = len ( s ) ans = 0 for i in range ( n ): for j in range ( i + 1 , n ): if check ( i , j ): ans = max ( ans , j - i + 1 ) return ansVideo walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.