-
Notifications
You must be signed in to change notification settings - Fork 0
Create 22. Generate Parentheses.md #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,155 @@ | ||
| # 進め方 | ||
|
|
||
| Step1 : 問題を解く。 | ||
|
|
||
| Step2 : 他の人のPRを参照し、コメントする。 | ||
|
|
||
| Step3 : 3回続けてエラーが出ないように書く。ドキュメントを参照する。 | ||
|
|
||
| # 実践 | ||
|
|
||
| ## Step1 | ||
|
|
||
| ### 思考ログ | ||
|
|
||
| 5m くらい | ||
|
|
||
| ```python | ||
| class Solution: | ||
| def generateParenthesis(self, n: int) -> List[str]: | ||
| result = [] | ||
| def helper(used_open, opening_count, parentheses): | ||
| if used_open >= n and opening_count <= 0: | ||
| result.append(parentheses) | ||
| return | ||
| if used_open < n: | ||
| helper(used_open + 1, opening_count + 1, parentheses + '(') | ||
| if opening_count > 0: | ||
| helper(used_open, opening_count - 1, parentheses + ')') | ||
|
|
||
| helper(0, 0, '') | ||
| return result | ||
| ``` | ||
|
|
||
| ```python | ||
| class Solution: | ||
| def generateParenthesis(self, n: int) -> List[str]: | ||
| result = [] | ||
| stack = [(0, 0, '')] | ||
| while stack: | ||
| used_open, opening_count, parentheses = stack.pop() | ||
| if used_open >= n and opening_count <= 0: | ||
| result.append(parentheses) | ||
| if used_open < n: | ||
| stack.append((used_open + 1, opening_count + 1, parentheses + '(')) | ||
| if opening_count > 0: | ||
| stack.append((used_open, opening_count - 1, parentheses + ')')) | ||
| return result | ||
| ``` | ||
|
|
||
| https://discord.com/channels/1084280443945353267/1233603535862628432/1263405280499073035 | ||
|
|
||
| これの言っていることが分かりました。 | ||
|
|
||
| 漏れなくダブりなく網羅するためにどうするか、を考えればよい。 | ||
|
|
||
| ループであれば、担当者は自分が取れる行動をすべて取って、それぞれ引き継ぐイメージ。 | ||
|
|
||
| ## Step2 | ||
|
|
||
| ### 同じ問題を解いた人のプルリクを見る | ||
|
|
||
| ざっとプルリク見た感じ、色々な書き方を試しているようだった。 | ||
|
|
||
| https://github.com/olsen-blue/Arai60/pull/54/files | ||
|
|
||
| - 自分のstep1の変数名は改善の余地がありそう。 | ||
| - 枝刈り。タスクを引継ぎするほうが判断するのか、される方が判断するのか。 | ||
| - 今回はするほうが判断したほうが読みやすそう。 | ||
| - 解法4は読めないけど理解したら得るものがありそう。 | ||
| - https://github.com/hroc135/leetcode/pull/50/files | ||
| - コピーの際の計算量。 | ||
| - Step1のソースはコピーが発生してますね。 | ||
| - 今回の問題では気にしなくても良さそうか? | ||
| - カタラン数は常識ではないらしい。 | ||
|
|
||
| https://github.com/nittoco/leetcode/pull/43 | ||
|
|
||
| - この人のstep3と似ている気がする。 | ||
|
|
||
| Step1の変数名を書き直す。 | ||
|
|
||
| Step1では残りがあれば追加としていたが、今回は使用した分と上限を比べて、という感じにした。今回のほうがコードがすっきりしている。 | ||
|
|
||
| ```python | ||
| class Solution: | ||
| def generateParenthesis(self, n: int) -> List[str]: | ||
| result = [] | ||
| stack = [(0, 0, '')] | ||
| while stack: | ||
| num_open, num_close, parentheses = stack.pop() | ||
| if num_open >= n and num_close >= n: | ||
| result.append(parentheses) | ||
| if num_open < n: | ||
| stack.append((num_open + 1, num_close, f'{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. レビューありがとうございます。 |
||
| if num_open > num_close: | ||
| stack.append((num_open, num_close + 1, f'{parentheses})')) | ||
| return result | ||
| ``` | ||
|
|
||
| 今回はjoinと+は大差なさそうに思える。 | ||
|
|
||
| > で、それに対して、「そう思っていたんですが実験した範囲では最適化が効くみたいです。」という返答は、なかなかに困って、というのも、次の疑問がわいてきます。「いつでもその最適化は行われるのか。インタープリターのバージョンに依存しないのか、たとえば、バージョンアップで最適化がなくなることはないのか。もしも、最適化がされることが保証されていないならば、そのように書いておいたとして、どういった場合に最適化されないのか。そのような仕様が仮にあるのだとしたら、そのドキュメントへのリンクをコメントで書いておいて欲しい。また、Python のバージョンが変わったときには、その最適化がされることが保証されていないならば、そのドキュメントをもう一回見て、バージョンによって仕様が変わっていないかどうかを確認するプロセスが必ず走るようにして欲しい。」で、ここまでの疑問にその場で答えられるならば、面接官は、なるほど、そうなんですね、といって、Python に詳しい人だと思うでしょう。で、仕様レベルで最適化が保証されているのだとしても、最低限コメントとして、「このような場合には最適化されることが保証される。どこどこ参照。」と書いておかないと、今後そのコードを読んでデバッグする人が、「むむ、もしかして、今回のタイムアウトの原因はここではないかな?」といって余計な実験をすることになるわけです。つまり、「あ、Python の文字列はイミュータブルなので、こうしたほうがいいですね。」は減点なしの評価で、まあ、思わずやっちゃうことあるよね、くらいの感覚です。 | ||
| 「この場合は最適化されることが仕様レベルで保証されているのでその旨のコメントを書き足しましょうか。」も減点はしにくいけれども、それだったら join で書き直しませんか、という気分ですね。つまり、気にしているのは、オーダーが正しく動くか、ではなくて、半年後に読んだ別の同僚が不安にならないか、環境の変化に対して頑健か、なのです。 | ||
| > | ||
|
|
||
| ```python | ||
| class Solution: | ||
| def generateParenthesis(self, n: int) -> List[str]: | ||
| if n == 0: | ||
| return [""] # ここをリストにしないとエラー | ||
| result = [] | ||
| for i in range(n): | ||
| for front in self.generateParenthesis(i): | ||
| for back in self.generateParenthesis(n - 1 - i): | ||
| result.append(f'({front}){back}') | ||
| return result | ||
| ``` | ||
|
|
||
| ## Step3 | ||
|
|
||
| ### 3回連続で再現 | ||
|
|
||
| ```python | ||
| class Solution: | ||
| def generateParenthesis(self, n: int) -> List[str]: | ||
|
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. @cache デコレータをつけると速くなりますね。 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. 文字列を丸ごとコピーしないようにするやつを、Python にしてみました。 from functools import cache
from typing import List, Callable
@cache
def generate_functions_to_append_parenthesis(n: int) -> List[Callable[[List[str]], None]]:
if n == 0:
return [lambda sb: None]
result = []
for i in range(n):
for a in generate_functions_to_append_parenthesis(i):
for b in generate_functions_to_append_parenthesis(n - i - 1):
def fn(sb: List[str], a=a, b=b) -> None:
sb.append("(")
a(sb)
sb.append(")")
b(sb)
result.append(fn)
return result
def generate_parenthesis(n: int) -> List[str]:
funcs = generate_functions_to_append_parenthesis(n)
result = []
for f in funcs:
sb = []
f(sb)
result.append("".join(sb))
return result |
||
| if n == 0: | ||
| return [''] | ||
| result = [] | ||
| for i in range(n): | ||
| for front in self.generateParenthesis(i): | ||
| for back in self.generateParenthesis(n - i - 1): | ||
| result.append(f'({front}){back}') | ||
| return result | ||
| ``` | ||
|
|
||
| 紙に書きながらデバッグしたらなんとなくわかった。 | ||
|
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. 言われてみればそうでした。 |
||
|
|
||
| ```python | ||
| class Solution: | ||
| def generateParenthesis(self, n: int) -> List[str]: | ||
| result = [] | ||
| stack = [(0, 0, '')] | ||
| while stack: | ||
| num_opens, num_closes, parenthesis = stack.pop() | ||
| if num_opens == n and num_closes == n: | ||
| result.append(parenthesis) | ||
| continue | ||
| if num_opens < n: | ||
| stack.append((num_opens + 1, num_closes, f'{parenthesis}(')) | ||
| if num_closes < num_opens: | ||
| stack.append((num_opens, num_closes + 1, f'{parenthesis})')) | ||
| return result | ||
| ``` | ||
|
|
||
| こっちは自信もって書ける。 | ||
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.
カタラン数は聞いたことありました?
大学受験でたまに出てくることがありますね。(知らなくても解ける程度。)
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.
知らなかったです。
思ったより応用範囲が広そうに思いました。
https://www.chugakujuken.com/koushi_blog/sakai/21731.html
https://ja.m.wikipedia.org/wiki/%E3%82%AB%E3%82%BF%E3%83%A9%E3%83%B3%E6%95%B0