-
Notifications
You must be signed in to change notification settings - Fork 0
Solved Arai60/22. Generate Parentheses #52
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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) | ||
| ``` | ||
|
|
||
| - と思ったが(())(())のような部分的に囲むやつが考慮できていないので失敗 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. こういう風にしたいならば、はじめの括弧と対応するやつは必ずあるはずなので、
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. どういう実装をイメージされているんでしょうか? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ご参考リンクです。 |
||
| - 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() | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. opened で開き括弧の数を表しているのは少しわかりにくいと思いました。自分で書くなら num_opens にします。 |
||
| if opened == n: | ||
| new_parenthesis = parenthesis + (opened - closed) * ")" | ||
| if new_parenthesis not in parentheses: | ||
| parentheses.add(new_parenthesis) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if 文不要で直接 add しても良いと思いました。 |
||
| 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: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. opened はこれまでに使った開き括弧の数で、opening はまだ対応する閉じ括弧がない開き括弧の数ということですよね。少し読み取りにくく感じました。下で書かれているようにこれまでに使った開き・閉じ括弧の数を考えた方がシンプルだと思いました。 |
||
| 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) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. これはちょっと大きすぎる見積もりです。
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ですね |
||
|
|
||
| ## 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)) | ||
| ``` | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
n == 0 で [""] のほうがいいでしょう。