View on NeetCode
View on LeetCode

Problem

Given a string s containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid.

An input string is valid if:

  1. Open brackets must be closed by the same type of brackets.
  2. Open brackets must be closed in the correct order.
  3. Every close bracket has a corresponding open bracket of the same type.

Example 1:

Input: s = "()"
Output: true

Example 2:

Input: s = "()[]{}"
Output: true

Example 3:

Input: s = "(]"
Output: false

Constraints:

  • 1 <= s.length <= 10^4
  • s consists of parentheses only '()[]{}'.

Solution

Approach: Stack

The key insight is to use a stack to track opening brackets. When we encounter a closing bracket, it must match the most recent unmatched opening bracket (the top of the stack).

Algorithm:

  1. Push opening brackets onto the stack
  2. For closing brackets, check if they match the top of the stack
  3. If they match, pop the stack; otherwise, return false
  4. At the end, the stack should be empty

Implementation

class Solution:
    def isValid(self, s: str) -> bool:
        stack = []
        # Mapping of closing to opening brackets
        matching = {')': '(', '}': '{', ']': '['}

        for char in s:
            if char in matching:
                # Closing bracket
                if not stack or stack[-1] != matching[char]:
                    return False
                stack.pop()
            else:
                # Opening bracket
                stack.append(char)

        # Valid only if all brackets are matched
        return len(stack) == 0

Alternative (Using Dictionary for Opening Brackets):

class Solution:
    def isValid(self, s: str) -> bool:
        stack = []
        closing_to_opening = {')': '(', '}': '{', ']': '['}
        opening = set(['(', '{', '['])

        for char in s:
            if char in opening:
                stack.append(char)
            elif char in closing_to_opening:
                if not stack or stack.pop() != closing_to_opening[char]:
                    return False

        return not stack

Complexity Analysis

  • Time Complexity: O(n), where n is the length of the string. We process each character once.
  • Space Complexity: O(n) in the worst case, when all characters are opening brackets and we push them all onto the stack.

Key Insights

  1. Stack for LIFO Matching: A stack is perfect for this problem because the most recent unmatched opening bracket must match the current closing bracket (Last-In-First-Out).

  2. Early Termination: If we encounter a closing bracket without a matching opening bracket on the stack, we can immediately return false.

  3. Complete Matching: After processing all characters, the stack must be empty. A non-empty stack means there are unmatched opening brackets.

  4. Three Failure Cases:

    • Closing bracket without matching opening (empty stack or wrong type)
    • Extra opening brackets (non-empty stack at end)
    • Wrong order (closing bracket doesn’t match top of stack)