Taro Logo

Generate Parentheses

Medium
1 views
a month ago

Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses. Examples:

Example 1: Input: n = 3 Output: ["((()))","(()())","(())()","()(())","()()()"]

Example 2: Input: n = 1 Output: ["()"]

Constraints: 1 <= n <= 8

Sample Answer
def generateParenthesis(n):
    """
    Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.
    For example, when n = 3, the output should be ["((()))","(()())","(())()","()(())","()()()"]
    """
    result = []

    def backtrack(s='', left=0, right=0):
        if len(s) == 2 * n:
            result.append(s)
            return
        if left < n:
            backtrack(s + '(', left + 1, right)
        if right < left:
            backtrack(s + ')', left, right + 1)

    backtrack()
    return result


# Naive Solution (Brute Force)
# Generate all possible sequences of '(' and ')' of length 2n.
# Check if each sequence is valid.
# Time complexity: O(2^(2n) * n)
# Space complexity: O(n) (for the string)

def generateParenthesis_naive(n):
    def generate_all(current, result):
        if len(current) == 2 * n:
            if valid(current):
                result.append(''.join(current))
        else:
            current.append('(')
            generate_all(current, result)
            current.pop()
            current.append(')')
            generate_all(current, result)
            current.pop()

    def valid(s):
        balance = 0
        for char in s:
            if char == '(': balance += 1
            else: balance -= 1
            if balance < 0: return False
        return balance == 0

    result = []
    generate_all([], result)
    return result


# Optimal Solution (Backtracking)
# Maintain a count of open and close brackets.
# Only add an open bracket if open < n
# Only add a close bracket if close < open
# Time complexity: O(4^n / sqrt(n))
# Space complexity: O(n)

# Example Usage
n = 3
print(generateParenthesis(n))


# Big O Runtime Analysis
# The optimal solution (backtracking) has a time complexity of O(4^n / sqrt(n)). This is because in the worst case, we explore all possible combinations of parentheses, but the number of valid combinations is bounded by the Catalan number, which is approximately 4^n / sqrt(n).

# Big O Space Usage Analysis
# The optimal solution has a space complexity of O(n). This is because the maximum depth of the recursion is n, and we store the generated string, which has a length of at most 2n, which is O(n).

# Edge Cases and How to Handle Them
# n = 1: Output should be ["()"]
# n = 2: Output should be ["(())", "()()"]
# n = 3: Output should be ["((()))", "(()())", "(())()", "()(())", "()()()"]
# n = 8: The algorithm should be able to handle this without exceeding memory limits or taking too long.