The problem
Given n, return every string of n pairs of round brackets that is well-formed: every ( is closed by a later ), and no prefix ever has more ) than (.
Input: n = 2
Output: ["(())", "()()"]With two pairs, those are the only balanced strings. ")(()" and "())(" fail because a ) appears before its partner. For n = 3 there are five.
How to think about it
Start with brute force. Generate all 2^(2n) strings of length 2n made of ( and ), then check each with a counter. For n = 3 that’s 64 strings for 5 answers; for n = 10 it’s over a million for 16,796. Almost everything you build is invalid.
The key insight: never build an invalid prefix. A string is balanced exactly when (a) it uses n opens and n closes, and (b) at every point, closes so far ≤ opens so far. Both rules can be checked while you build, one character at a time, using two counters:
- you may add
(ifopen < n(you still have opens left); - you may add
)only ifclose < open(there is an unmatched(to close).
If you follow these two rules, every string that reaches length 2n is valid. There is nothing to filter at the end, and no dead branch is ever explored.
Which pattern is this? Backtracking with pruning. The tell is “generate all valid strings / arrangements” where validity can be checked on a prefix. Here the “undo” step is free: the code passes s + '(' as a new string, so there’s nothing to pop.
Approach
bt(s, open_, close): iflen(s) == 2n, recordsand return.- If
open_ < n, recurse withs + '('andopen_ + 1. - If
close < open_, recurse withs + ')'andclose + 1. - Start with
bt('', 0, 0).
Solution
def generate_parenthesis(n):
res = []
def bt(s, open_, close):
if len(s) == 2 * n: res.append(s); return
if open_ < n: bt(s + '(', open_ + 1, close) # an open bracket is still available
if close < open_: bt(s + ')', open_, close + 1) # only close what's been opened
bt('', 0, 0); return res
# generate_parenthesis(3) -> 5 strings ("((()))","(()())","(())()","()(())","()()()")Dry run
On n = 2, every call in order:
s |
open_ |
close |
Allowed next |
|---|---|---|---|
'' |
0 | 0 | ( only |
( |
1 | 0 | ( or ) |
(( |
2 | 0 | ) only (no opens left) |
(() |
2 | 1 | ) only |
(()) |
2 | 2 | full: record |
() |
1 | 1 | ( only (close == open_) |
()( |
2 | 1 | ) only |
()() |
2 | 2 | full: record |
Eight calls, two answers, and not one invalid string was ever built.
Complexity
- Time: O(4ⁿ / √n). The number of answers is the n-th Catalan number, which grows like 4ⁿ / (n^1.5), and each takes O(n) to build. Because no branch is wasted, the work is proportional to the output.
- Space: O(n) for the recursion depth (2n levels), not counting the output.
Edge cases and pitfalls
- n = 1 gives
["()"]. If n = 0 were allowed, the code returns[""]. - Using
close < ninstead ofclose < open_lets you write)too early and produces invalid strings like"())(". - Checking validity only at the end works but turns this into the brute force.
- Building with a list is faster for big n: append, recurse, pop. The string version is simpler and fine at interview sizes.
Related problems
- Valid Parentheses (LeetCode 20): checking one string, instead of generating all of them.
- Letter Combinations of a Phone Number (LeetCode 17): another build-a-string backtracking with no undo step.
- Valid Parenthesis String (LeetCode 678): the same open/close counting idea, with wildcards.
- 1.Subsets (LeetCode 78)
- 2.Subsets II (LeetCode 90)
- 3.Permutations (LeetCode 46)
- 4.Permutations II (LeetCode 47)
- 5.Combinations (LeetCode 77)
- 6.Combination Sum (LeetCode 39)
- 7.Combination Sum II (LeetCode 40)
- 8.Generate Parentheses (LeetCode 22)
- 9.Letter Combinations of a Phone Number (LeetCode 17)
- 10.Palindrome Partitioning (LeetCode 131)
- 11.Word Search (LeetCode 79)
- 12.N-Queens (LeetCode 51 / 52)