Finding all combinations of well-formed brackets
Data Structures & Algorithms practice on Codemia
Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.
Introduction
Generating all well-formed bracket strings is a standard backtracking problem because the search space is large but highly structured. A valid result must stay balanced at every prefix, not only at the end. Once you build the solution around that rule, the algorithm becomes much cleaner than brute-force generation.
Think in Terms of Valid Prefixes
A bracket string is well formed if two conditions hold:
- you never close more brackets than you have opened so far,
- and by the end, the number of opens equals the number of closes.
That means you do not have to generate every possible string of length 2n and test it afterward. You can build the string step by step and reject invalid prefixes immediately.
That pruning idea is the reason backtracking is the usual solution.
Backtracking Is the Most Direct Approach
At any step, you may add an opening bracket if you still have one available. You may add a closing bracket only if it would not make the prefix invalid.
This produces:
- '
((()))' - '
(()())' - '
(())()' - '
()(())' - '
()()()'
The important property is that the recursion never descends into states that are already invalid.
Why Brute Force Is Wasteful
If you try every length-2n sequence of opening and closing brackets, you create an enormous number of invalid candidates. Most of those strings fail quickly, yet a brute-force generator still spends time constructing them.
Backtracking saves work by encoding the correctness rules directly into the construction process. That does not make the problem easy in an asymptotic sense, because the number of valid results still grows quickly, but it removes a large amount of pointless search.
A Dynamic Programming Perspective Also Exists
There is another way to understand the problem: well-formed bracket strings can be composed from smaller well-formed strings. That leads to a dynamic programming formulation.
If dp[i] stores all valid strings with i pairs, then every result in dp[i] can be formed by wrapping one smaller valid string and concatenating another.
This approach is mathematically elegant and useful for understanding the recursive structure of the set. In interviews and day-to-day coding, though, backtracking is usually the simpler implementation.
Generalizing Beyond Parentheses
The same logic extends to other matched delimiters, but you need to be clear about the grammar you want. If there is only one bracket type, the validity rule is simple balance. If you allow multiple bracket types such as parentheses and square brackets, the problem becomes richer because matching type order now matters as well.
So the “well-formed brackets” pattern is a gateway problem for recursive generation more generally.
Complexity Follows the Output Size
The number of valid strings with n pairs is the nth Catalan number. That means the result count itself grows combinatorially. So the algorithm is expensive mainly because there are genuinely many correct answers to emit.
This is an important mindset shift: for enumeration problems, large runtime is often driven by large output, not by a poor algorithm.
Common Pitfalls
- Generating all length-
2nstrings and validating them afterward instead of pruning during generation. - Allowing a closing bracket when no unmatched opening bracket exists.
- Forgetting that every completed result must have exactly
nopens andncloses. - Confusing the elegant dynamic-programming formulation with the easiest implementation for production code.
- Treating multiple bracket types as if the single-type balance rule were sufficient.
Summary
- The key idea is to build only valid prefixes.
- Backtracking is the most direct way to generate all well-formed bracket strings.
- Dynamic programming also works and reveals the compositional structure of the problem.
- The number of valid outputs grows according to Catalan numbers.
- Enumeration is expensive because the output set itself becomes large quickly.
Related reading
- Finding all cycles in a directed graph
- Finding all cycles in an undirected graph
- Finding all disconnected subgraphs in a graph
- Finding all empty triangles
- Finding all permutations that match a set of rules
- Finding all permutations to get the given sum Coin change problem
- Finding all possible combinations of numbers to reach a given sum
- Finding all possible combined plus and minus sums of n arguments?

DSA Fundamentals
Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Data Structures & Algorithms practice on Codemia
Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.