A string is valid if: (1) open brackets are closed by the same type of bracket, (2) open brackets are closed in the correct order, and (3) every close bracket has a corresponding open bracket of the same type.
Example 1
Input: s = "()[]"Output:true
Example 2
Input: s = "(]"Output:false
Example 3
Input: s = "([)]"Output:false// Interleaved โ wrong nesting order
Constraints
โข 1 โค s.length โค 10โดโข s consists of parentheses only: '()[]{}'
02
Section Two ยท Approach 1
Brute Force โ Repeated Replace
Repeatedly scan the string and remove adjacent matching pairs (), [], {}. If the string becomes empty, it's valid. Each pass removes at most n/2 pairs, so up to n/2 passes โ O(nยฒ).
Problem: O(nยฒ) from repeated string scanning. A stack processes every character in a single O(n) pass โ push openers, pop and match closers.
Java โ Repeated Replace
classSolution {
publicbooleanisValid(String s) {
int prev = -1;
while (s.length() != prev) {
prev = s.length();
s = s.replace("()", "").replace("[]", "").replace("{}", "");
}
return s.isEmpty();
}
}
Python โ Repeated Replace
classSolution:
defisValid(self, s: str) -> bool:
while'()'in s or'[]'in s or'{}'in s:
s = s.replace('()', '').replace('[]', '').replace('{}', '')
return s == ''
Metric
Value
Time
O(nยฒ)
Space
O(n) โ string copies
03
Section Three ยท Approach 2
Stack โ O(n)
Scan left to right. When you see an opening bracket, push it onto the stack. When you see a closing bracket, pop the stack and check if the popped opener matches. If the stack is empty when you try to pop, or the bracket doesn't match, it's invalid. At the end, the stack must be empty.
๐ก Mental model: Think of stacking plates. Each opening bracket adds a plate. Each closing bracket takes a plate off โ but only if it's the right color. If you try to remove a plate that doesn't match, or there are no plates to remove, the stack is broken. At the end, a clean table (empty stack) means everything matched.
Algorithm
Step 1: Create an empty stack and a map: { ')':'(', ']':'[', '}':'{' }.
Step 2: For each character: if it's an opener, push it. If it's a closer, pop and check match.
Step 3: If stack is empty on pop or pop doesn't match โ return false.
Step 4: After scanning, return stack.isEmpty().
๐ฏ When to recognize this pattern:
Any problem involving matching nested pairs โ parentheses, HTML tags, function calls โ think stack.
The LIFO property naturally handles nesting: the most recently opened thing must be closed first.
classSolution:
defisValid(self, s: str) -> bool:
stack = []
match = {')': '(', ']': '[', '}': '{'}
for c in s:
if c in match:
if not stack or stack.pop() != match[c]:
returnFalseelse:
stack.append(c)
return len(stack) == 0
06
Section Six ยท Analysis
Complexity Analysis
Approach
Time
Space
Trade-off
Repeated Replace
O(nยฒ)
O(n)
Simple but slow string rebuilds
Stack โ optimal
O(n)
O(n)
One pass. Stack holds at most n/2 openers.
07
Section Seven ยท Edge Cases
Edge Cases & Pitfalls
Case
Input
Why It Matters
Odd length
"("
Odd-length strings are always invalid โ brackets come in pairs.
Single pair
"()"
Minimal valid input.
Closer first
")"
Stack empty on first pop โ false.
Wrong nesting
"([)]"
Pop gives '[' but closer is ')' โ mismatch.
Deep nesting
"(((())))"
Stack grows to n/2, then unwinds. Valid.
โ Common Mistake: Forgetting to check stack.isEmpty() at the end. A string like "((" passes all pop checks (no closers to fail) but leaves unmatched openers on the stack. The final empty check catches this.