Example 1
ready- Input
s = "()[]{}"- Expected
true- Why
- each pair closes cleanly before the next begins
One misplaced bracket can change what an entire program means. Here, every character is a bracket, so every one has to land in exactly the right place.
You're given a string s containing only (, ), [, ], {, and }. An
opener is a left bracket such as (; a closer is its right-bracket partner,
such as ).
Return true when every opener is closed by the same bracket type and the pairs close
in the correct order. Return false otherwise.
A few ground rules:
1 <= s.length <= 100_000s consists only of ()[]{}.Hints
Repeatedly erase every adjacent (), [], and {}. A valid string eventually
disappears. What does that cost when 50,000 pairs are nested inside one another?
A stack is a collection where the last item added is the first one removed. Store each opener as you scan from left to right, then compare each closer with the opener on top.
When you see (, push ) rather than (. Then every closer has one job: equal the
top of the stack. At the end, the stack must also be empty.
Could you reject a bad string the moment it becomes impossible, while reading its characters only once?
Visible cases
s = "()[]{}"trues = "(]"falses = "([)]"falseInterview signal
Start with the most literal observation: (), [], and {} are complete pairs. Remove
all adjacent complete pairs, then repeat. A valid string eventually becomes empty; an
invalid one gets stuck with characters left over.
def is_valid(s: str) -> bool:
remaining = s
while True:
reduced = remaining.replace("()", "").replace("[]", "").replace("{}", "")
if len(reduced) == len(remaining):
return reduced == ""
remaining = reducedA deeply nested input may expose only one removable pair per pass. Each pass scans a string of up to n characters, and there can be O(n) passes.
Time O(n²) in the worst case. Space O(n) for the rebuilt strings.
A stack is a last-in, first-out collection. That order matches nested brackets: the latest opener must be the first one closed.
You could store openers and translate each closer back to its partner. There's an even
cleaner move: store the closer you expect. When ( arrives, push ). When a closer
arrives, it must equal the top item. A missing or different top makes the answer false
immediately.
def is_valid(s: str) -> bool:
expected: list[str] = []
closer_for = {"(": ")", "[": "]", "{": "}"}
for bracket in s:
if bracket in closer_for:
expected.append(closer_for[bracket])
elif not expected or expected.pop() != bracket:
return False
return not expectedThe final emptiness check matters. A string made only of openers never causes a mismatch during the scan, but it still leaves unfinished pairs behind.
Time O(n) — each bracket is pushed or checked once. Space O(n) in the deepest nesting case.
Ready to run.
3 cases are queued.
Visible testcase
Case 1
Expected
true