diff --git a/22. Generate Parentheses.md b/22. Generate Parentheses.md new file mode 100644 index 0000000..b3e2e55 --- /dev/null +++ b/22. Generate Parentheses.md @@ -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}(')) + 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]: + 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 +``` + +紙に書きながらデバッグしたらなんとなくわかった。 + +```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 +``` + +こっちは自信もって書ける。