Befitting Brackets

Befitting Brackets

A string contains three kinds of brackets: (), [], and {}. Return true if they are properly balanced and nested — every closer matches the most recent unmatched opener of the same type.

befittingBrackets(s)

Input format: a single string of brackets.

Examples

"(){}[]"      // → true
"([]{})"      // → true
"[(])"        // → false

Walkthrough

Thinking it through

The natural extension of single-bracket matching: three types now, and they nest. The rule isn't "equal counts of each type" — a closer must match the most recently opened, still-unclosed bracket of the same type. "Most recent, still open" is the giveaway for last-in-first-out.

Why a single counter no longer works

With one type, every opener was interchangeable. With three, that breaks: [(]) has one of each bracket type — balanced by count — but the nesting is crossed, since ) appears before the ] that should close first. Counting can't detect crossing; you need to know which brackets are open, in what order.

Why a stack captures exactly that order

A stack remembers the sequence of open brackets, most recent on top — exactly "most recent, still open."

  1. On an opener, push it.
  2. On a closer, check the top of the stack — it must match in type. If it does, pop. If not, or the stack is empty, invalid.
  3. Anything left on the stack after scanning means unclosed openers.

Why this catches crossing, not just imbalance

Back to [(]): push [, push (, hit ] — but the top is (, not [, so the match fails immediately, correctly flagging the crossing. The stack enforces that closers resolve brackets in strictly reverse order of opening — exactly what "properly nested" means, caught the moment it breaks rather than through some more complicated global count.

main.py
Console
Press Run to execute your code, or Submit to test and complete this problem.