Open and Shut
An easy Python interview practice problem on DataDriven. Write the Python and run it against test cases, with instant feedback.
- Domain
- Python
- Difficulty
- easy
- Seniority
- L5
- Asked in
- Walmart
Problem
A config parser checks each config string before handing it to the evaluator. A config string s mixes the three bracket pairs (), [] and {} with ordinary config text, and only the brackets decide whether it is well formed. A bracket opened inside another must close before the outer one does. Return whether every bracket in s belongs to a matching pair of its own type, nested in the right order.
Every opener is a promise. The order you close them in decides whether it holds.
Example 1
Input:
s = "({[]})"Output:
true
Example 2
Input:
s = "([)]"
Output:
false
Worked solution and explanation
What this really is
Strip the validator framing and this is a last-in-first-out matching problem: the most recently opened bracket must be the next one to close. Anyone can count brackets and confirm the totals line up. What actually separates candidates is catching the two strings where counting lies. In '([)]' the counts are perfect but the square bracket closes while a parenthesis is still open, so the nesting is wrong. In '({' nothing ever mismatches, yet two openers are left dangling at the end. Miss either and your validator waves malformed input straight through to the evaluator.
Break down the requirements
Step 1: Map closing brackets to their openers
Build a dict mapping each closer to its opener: ) to (, ] to [, } to {. One lookup replaces a chain of if/elif comparisons and makes the match check a single expression.
Step 2: Push openers, pop and match closers
Walk the string once. An opener gets pushed. A closer must find a matching opener on top of the stack, else you can bail immediately. Anything that is neither is filler and gets skipped, which is what makes 'hello (world)' work without special-casing letters.
Step 3: Verify the stack is empty at the end
The loop alone is not enough. A string like '({' never triggers a mismatch because no closer ever arrives. The only way to catch leftover openers is to confirm the stack is empty once the loop ends.
The solution
def is_balanced(s):
stack = []
matching = {')': '(', ']': '[', '}': '{'}
for char in s:
if char in '([{':
stack.append(char)
elif char in matching:
if not stack or stack[-1] != matching[char]:
return False
stack.pop()
return not stackOne pass, O(n) time; the closing check covers both the wrong-type and the empty-stack cases.
Common follow-up questions
- What if you needed to return the position of the first mismatch instead of a boolean? (Tests tracking the index alongside the stack operations.)
- How would you handle quoted regions that disable bracket matching? (Tests a state machine that toggles an 'inside quotes' flag so brackets inside a string literal are ignored.)
- If the string contained only one type of bracket, what could you drop? (Tests simplifying to a single counter instead of a stack.)