diff --git a/Python3/22. Generate Parentheses.md b/Python3/22. Generate Parentheses.md new file mode 100644 index 0000000..3b0b9e3 --- /dev/null +++ b/Python3/22. Generate Parentheses.md @@ -0,0 +1,131 @@ +## Step 1. Initial Solution + +- 再帰的にやれば行けそう + - 前のやつの全体を囲うか()を前後につけるか + + ```python + class Solution: + def generateParenthesis(self, n: int) -> List[str]: + if n == 1: + return ["()"] + parentheses = set() + for parenthesis in self.generateParenthesis(n-1): + parentheses.add("".join(["(", parenthesis, ")"])) + parentheses.add("".join(["()", parenthesis])) + parentheses.add("".join([parenthesis, "()"])) + return list(parentheses) + ``` + + - と思ったが(())(())のような部分的に囲むやつが考慮できていないので失敗 +- Stackで(の個数と)の個数を保持しながら処理していく方針に変更 + + ```python + class Solution: + def generateParenthesis(self, n: int) -> List[str]: + parentheses = set() + parenthesis_opened_closed = [("", 0, 0)] + while parenthesis_opened_closed: + parenthesis, opened, closed = parenthesis_opened_closed.pop() + if opened == n: + new_parenthesis = parenthesis + (opened - closed) * ")" + if new_parenthesis not in parentheses: + parentheses.add(new_parenthesis) + continue + parenthesis += "(" + opened += 1 + for i in range(opened - closed + 1): + parenthesis_opened_closed.append((parenthesis + i * ")", opened, closed + i)) + + return list(parentheses) + ``` + +- あまり直感的ではないように感じたので(の数ごとに階層を区切って処理してみる + + ```python + class Solution: + def generateParenthesis(self, n: int) -> List[str]: + parentheses = [] + parentheses_and_openings = [("", 0)] + for opened in range(1, n + 1): + new_parentheses_and_openings = [] + for parenthesis, opening in parentheses_and_openings: + parenthesis += "(" + opening += 1 + if opened == n: + parentheses.append(parenthesis + opening * ")") + continue + for closing in range(opening + 1): + new_parentheses_and_openings.append((parenthesis + closing * ")", opening - closing)) + parentheses_and_openings = new_parentheses_and_openings + return parentheses + ``` + + +### Complexity Analysis + +- 時間計算量:O(n^n) + - 各層の計算量が<2k C k + - 2k C k < k^k なので大体このくらい? +- 空間計算量:O(n^n) + +## Step 2. Alternatives + +- https://github.com/akmhmgc/arai60/pull/47/files#diff-49770f8b73d94c3a97c07766ce83932c7e9fc1168820a224bd14923bdfd5c876R10 + - (の追加と)の追加を各ループで行う方法 + - 確かにこの方法は分かりやすい + - popしてから次の分岐に行くから同じインスタンスを参照し続けられてメモリ効率も良い +- https://github.com/shintaro1993/arai60/pull/57/files#r2485774505 + - こちらでも同じことが言われていた +- https://github.com/shintaro1993/arai60/pull/57/files#diff-6c927ad20d197fb3d8c10182f5287498c084aca20e6c323cc144aa0ac3cbf4daR10 + - 全体的に自分のように各作業者がfor文を回してstackに乗せるような方法は少なそう + - 一つずつstackしていく方が分かりやすいのか? +- https://github.com/fhiyo/leetcode/pull/53/files#diff-9ca14f2eb9507a65f01cd60a9947537f09b5daeede1ef28ee1d790d8d76adb56R11 + - カタラン数なるものがあるらしい + - 2n C n / (n + 1)なので今回の計算量は(n+1) x Cnと書けそう + - スターリングの公式を使うと $n! = \sqrt{2 \pi n}(\frac{n}{e})^n$なので + + $$ + _{2n}C_n = \frac{1}{\sqrt{\pi n}} 2^{2n} + $$ + + - よって今回の計算量はO(n^(-1/2) *4^n)と書けそう +- https://github.com/fhiyo/leetcode/pull/53/files#diff-9ca14f2eb9507a65f01cd60a9947537f09b5daeede1ef28ee1d790d8d76adb56R257 + - generatorを使って作り上げていく方法 + - 他にもcacheを使うかとのトレードオフも判断していて参考になる + +## Step 3. Final Solution + +- 一つずつStack + +```python +class Solution: + def generateParenthesis(self, n: int) -> List[str]: + paren_opened_closed = [('', 0, 0)] + parens = [] + while paren_opened_closed: + paren, opened, closed = paren_opened_closed.pop() + if opened == n: + parens.append(paren + (opened - closed) * ')') + continue + paren_opened_closed.append((paren + '(', opened + 1, closed)) + if opened > closed: + paren_opened_closed.append((paren + ')', opened, closed + 1)) + return parens +``` + +- generatorを使う方法 + - どうやらLeetCode上ではあまり速くない上にメモリ効率も大きく変わらないよう + +```python +class Solution: + def generateParenthesis(self, n: int) -> List[str]: + def generate_parens(opened: int, closed: int) -> Iterator[str]: + if opened == n: + yield ')' * (opened - closed) + return + if opened > closed: + yield from (')' + s for s in generate_parens(opened, closed + 1)) + yield from ('(' + s for s in generate_parens(opened + 1 , closed)) + + return list(generate_parens(0, 0)) +```