Brute Force (Repeated Removal)
Time O(nΒ²)Space O(n)A valid string can always be fully reduced by repeatedly deleting any adjacent matching pair β (), [], or {} β until nothing removable is left. If what remains is empty, the string was valid.
class Solution: def isValid(self, s: str) -> bool: prev_len = -1 while len(s) != prev_len: prev_len = len(s) s = s.replace('()', '').replace('[]', '').replace('{}', '') return len(s) == 0Each pass scans the string in O(n), and in the worst case (deeply nested brackets like ((((...))))) it takes O(n) passes to fully collapse β O(nΒ²) total, plus the wasted work of rebuilding a new string on every pass.
Stack
OptimalTime O(n)Space O(n)Scan left to right with a stack. Every opening bracket gets pushed. Every closing bracket must match whatever is currently on top of the stack β thatβs exactly the βmost recently opened, must close firstβ rule, which is what a stack gives you for free.
class Solution: def isValid(self, s: str) -> bool: pairs = {')': '(', ']': '[', '}': '{'} stack = [] for ch in s: if ch in pairs: if not stack or stack.pop() != pairs[ch]: return False else: stack.append(ch) return not stackTracing s = "{[()]}":
'{' is an opener β push it. stack = ['{']
If a closing bracket ever finds a mismatched (or missing) top of stack, we return False immediately β thatβs what catches cases like "(]". Complexity: each character is pushed and popped at most once β O(n) time, O(n) space for the stack in the worst case (all openers).