Longest Balanced Substring
Longest Balanced Substring
Category
Strings
Difficulty
Hard
Problem Statement
Given a string consisting of only ( and ) characters, find the length of the longest contiguous substring that has balanced (valid) parentheses. A balanced substring has every opening parenthesis matched with a corresponding closing parenthesis in the correct order.
Intuition
A stack-based approach naturally handles parenthesis matching. By pushing indices onto the stack, we can compute the length of valid substrings when we find matching pairs. The key insight is to keep the index of the last unmatched position on the stack as a reference point. Alternatively, a two-pass approach (left-to-right and right-to-left) with counters avoids extra space entirely.
Approach
Stack-Based Approach
- Initialize a stack with
-1(as a base for length calculations). - Iterate through the string:
a. If the character is
(, push its index onto the stack. b. If the character is):- Pop from the stack.
- If the stack is now empty, push the current index as the new base.
- Otherwise, compute the length as
currentIndex - stack.top()and update the maximum.
- Return the maximum length.
Two-Pass Approach (O(1) Space)
- Left-to-right: Track
openandclosecounters. When they are equal, record the length. Whenclose > open, reset both to 0. - Right-to-left: Track
openandclosecounters. When they are equal, record the length. Whenopen > close, reset both to 0. - Return the maximum length found across both passes.
Pseudocode
// Stack approach
function longestBalancedSubstring(string):
maxLength = 0
stack = [-1]
for i from 0 to length(string) - 1:
if string[i] == '(':
stack.push(i)
else:
stack.pop()
if stack is empty:
stack.push(i)
else:
maxLength = max(maxLength, i - stack.top())
return maxLength
// Two-pass approach
function longestBalancedSubstring(string):
maxLength = 0
// Left to right
open = 0, close = 0
for char in string:
if char == '(': open++
else: close++
if open == close:
maxLength = max(maxLength, 2 * close)
if close > open:
open = 0; close = 0
// Right to left
open = 0, close = 0
for char in reverse(string):
if char == '(': open++
else: close++
if open == close:
maxLength = max(maxLength, 2 * open)
if open > close:
open = 0; close = 0
return maxLength
Time & Space Complexity
- Stack approach: O(n) time, O(n) space.
- Two-pass approach: O(n) time, O(1) space.
Both approaches make a constant number of passes through the string.
Key Insights
- The stack stores indices, not characters. This is essential for computing substring lengths.
- Initializing the stack with
-1handles the case where a valid substring starts at index 0. - The two-pass approach is needed because a single left-to-right pass cannot detect the longest balanced substring ending with excess opening parentheses (e.g.,
(()). The reverse pass catches these cases. - The problem is equivalent to finding the longest valid parentheses, a classic dynamic programming and stack problem.
Edge Cases
- Empty string: return 0.
- No valid pairs (e.g.,
)))(): return 0. - Entire string is balanced: return the full length.
- Single character: return 0.
- Nested balanced substrings (e.g.,
(())): the entire string is the answer. - Adjacent balanced substrings (e.g.,
()()): the combined length is the answer.