From bd5431408c5c883f625e4477d47c4a3547e31364 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 2 Oct 2026 05:59:05 +0000 Subject: [PATCH] feat: add solution for 0022. Generate Parentheses --- .../0022-generate-parentheses/analysis.md | 60 +++++++++++++++ problems/0022-generate-parentheses/problem.md | 44 +++++++++++ .../0022-generate-parentheses/solution.go | 30 ++++++++ .../solution_test.go | 77 +++++++++++++++++++ 4 files changed, 211 insertions(+) create mode 100644 problems/0022-generate-parentheses/analysis.md create mode 100644 problems/0022-generate-parentheses/problem.md create mode 100644 problems/0022-generate-parentheses/solution.go create mode 100644 problems/0022-generate-parentheses/solution_test.go diff --git a/problems/0022-generate-parentheses/analysis.md b/problems/0022-generate-parentheses/analysis.md new file mode 100644 index 0000000..49a04c6 --- /dev/null +++ b/problems/0022-generate-parentheses/analysis.md @@ -0,0 +1,60 @@ +# 0022. Generate Parentheses + +[LeetCode Link](https://leetcode.com/problems/generate-parentheses/) + +Difficulty: Medium +Topics: String, Dynamic Programming, Backtracking, Bracket Sequences +Acceptance Rate: 79.3% + +## Hints + +### Hint 1 + +You need to produce *every* valid string, not count them. Whenever a problem asks you to enumerate all combinations, think about building the answer one character at a time and exploring choices recursively. + +### Hint 2 + +Use backtracking: at each position you can either place `(` or `)`. Instead of generating all `2^(2n)` strings and filtering, try to prune invalid branches *while* building. What information do you need to track to know whether a choice is still legal? + +### Hint 3 + +Track two counters: `open` (how many `(` used) and `close` (how many `)` used). +- You may add `(` only if `open < n`. +- You may add `)` only if `close < open` (there is an unmatched `(` to close). + +With these two rules, every completed string of length `2n` is automatically valid — no filtering needed. + +## Approach + +We build strings recursively with a shared byte buffer of length `2n`. + +1. Start with an empty buffer, `open = 0`, `close = 0`. +2. If the buffer is full (`open + close == 2n`), record a copy of it as an answer. +3. Otherwise: + - If `open < n`, write `(` at the current position and recurse with `open + 1`. + - If `close < open`, write `)` at the current position and recurse with `close + 1`. +4. Because we overwrite the same position on each branch, no explicit "undo" step is needed. + +**Why it works:** A parentheses string is well-formed iff every prefix has at least as many `(` as `)`, and the totals are equal. Rule `close < open` enforces the prefix condition; rule `open < n` plus the length `2n` forces the totals to be equal. Every branch we explore leads to at least one valid string, so we never waste work on dead ends. + +**Example (n = 2):** + +``` +"" -> "(" -> "((" -> "(()" -> "(())" + -> "()" -> "()(" -> "()()" +``` + +Trying `(` before `)` at each step yields results in lexicographic order, matching the example output. + +## Complexity Analysis + +Time Complexity: O(4^n / √n) — the number of valid strings is the n-th Catalan number `C(n) ~ 4^n / (n^{3/2}·√π)`, and each takes O(n) to copy into the result. +Space Complexity: O(n) auxiliary (recursion depth and buffer of size `2n`), excluding the O(4^n / √n) output. + +## Edge Cases + +- **n = 1:** The smallest input; only `"()"` is valid. Make sure the base case triggers correctly. +- **Never closing before opening:** Strings like `")("` must never be generated — guarded by `close < open`. +- **Too many opens:** Strings like `"((("` for n = 2 must be cut off — guarded by `open < n`. +- **Larger n (up to 8):** Output grows to 1430 strings; the pruned backtracking handles this easily, but naive generate-and-filter (`2^16` strings) is wasteful. +- **Buffer reuse:** When reusing a byte slice, convert to `string` at the leaf so each result is an independent copy. diff --git a/problems/0022-generate-parentheses/problem.md b/problems/0022-generate-parentheses/problem.md new file mode 100644 index 0000000..3a23e93 --- /dev/null +++ b/problems/0022-generate-parentheses/problem.md @@ -0,0 +1,44 @@ +--- +number: "0022" +frontend_id: "22" +title: "Generate Parentheses" +slug: "generate-parentheses" +difficulty: "Medium" +topics: + - "String" + - "Dynamic Programming" + - "Backtracking" + - "Bracket Sequences" +acceptance_rate: 7935.0 +is_premium: false +created_at: "2026-10-02T05:58:06.797565+00:00" +fetched_at: "2026-10-02T05:58:06.797565+00:00" +link: "https://leetcode.com/problems/generate-parentheses/" +date: "2026-10-02" +--- + +# 0022. Generate Parentheses + +Given `n` pairs of parentheses, write a function to _generate all combinations of well-formed parentheses_. + + + +**Example 1:** + + + **Input:** n = 3 + **Output:** ["((()))","(()())","(())()","()(())","()()()"] + + +**Example 2:** + + + **Input:** n = 1 + **Output:** ["()"] + + + + +**Constraints:** + + * `1 <= n <= 8` diff --git a/problems/0022-generate-parentheses/solution.go b/problems/0022-generate-parentheses/solution.go new file mode 100644 index 0000000..c66f747 --- /dev/null +++ b/problems/0022-generate-parentheses/solution.go @@ -0,0 +1,30 @@ +package main + +// Approach: backtracking with two counters. +// Place '(' while open < n, and ')' while close < open. Every string of +// length 2n built under these rules is well-formed, so no filtering is needed. +// Time: O(4^n / sqrt(n)), Space: O(n) auxiliary. +func generateParenthesis(n int) []string { + result := []string{} + buf := make([]byte, 2*n) + + var backtrack func(open, close int) + backtrack = func(open, close int) { + pos := open + close + if pos == 2*n { + result = append(result, string(buf)) + return + } + if open < n { + buf[pos] = '(' + backtrack(open+1, close) + } + if close < open { + buf[pos] = ')' + backtrack(open, close+1) + } + } + + backtrack(0, 0) + return result +} diff --git a/problems/0022-generate-parentheses/solution_test.go b/problems/0022-generate-parentheses/solution_test.go new file mode 100644 index 0000000..c1804ca --- /dev/null +++ b/problems/0022-generate-parentheses/solution_test.go @@ -0,0 +1,77 @@ +package main + +import ( + "sort" + "testing" +) + +func isWellFormed(s string) bool { + balance := 0 + for _, c := range s { + if c == '(' { + balance++ + } else { + balance-- + } + if balance < 0 { + return false + } + } + return balance == 0 +} + +func TestGenerateParenthesis(t *testing.T) { + tests := []struct { + name string + n int + expected []string + }{ + {"example 1: n = 3", 3, []string{"((()))", "(()())", "(())()", "()(())", "()()()"}}, + {"example 2: n = 1", 1, []string{"()"}}, + {"edge case: n = 2", 2, []string{"(())", "()()"}}, + {"edge case: n = 4", 4, []string{ + "(((())))", "((()()))", "((())())", "((()))()", "(()(()))", + "(()()())", "(()())()", "(())(())", "(())()()", "()((()))", + "()(()())", "()(())()", "()()(())", "()()()()", + }}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := generateParenthesis(tt.n) + got := append([]string(nil), result...) + want := append([]string(nil), tt.expected...) + sort.Strings(got) + sort.Strings(want) + if len(got) != len(want) { + t.Fatalf("got %d results %v, want %d results %v", len(got), got, len(want), want) + } + for i := range got { + if got[i] != want[i] { + t.Errorf("got %v, want %v", got, want) + break + } + } + }) + } +} + +func TestGenerateParenthesisCatalanCount(t *testing.T) { + catalan := []int{1, 1, 2, 5, 14, 42, 132, 429, 1430} + for n := 1; n <= 8; n++ { + result := generateParenthesis(n) + if len(result) != catalan[n] { + t.Errorf("n=%d: got %d results, want %d", n, len(result), catalan[n]) + } + seen := make(map[string]bool) + for _, s := range result { + if len(s) != 2*n || !isWellFormed(s) { + t.Errorf("n=%d: invalid string %q", n, s) + } + if seen[s] { + t.Errorf("n=%d: duplicate string %q", n, s) + } + seen[s] = true + } + } +}