Create Word Break.md#8
Conversation
| return rec(0) | ||
| ``` | ||
|
|
||
| おそらくあってはいるが、スライスでコピーが作成されるため、大きな例でTLEを起こす。 |
There was a problem hiding this comment.
この例はすでに到達可能なことがわかっているものを再計算するのは筋が悪いという意味で捉えればよいのでしょうか。
| return True | ||
|
|
||
| for word in wordDict: | ||
| if s[index:index+len(word)] == word: |
There was a problem hiding this comment.
https://docs.python.org/3/library/stdtypes.html#str.startswith
startswith のドキュメントを読みましょう。
There was a problem hiding this comment.
存在知らなかったのも、調べなかったのも罪が重いですね……。
if s[index:index+len(word)] == word:を
if s.startswith(word, i):に置換してACし直しました。(当然ながら早くなりました)
| ```python3 | ||
| class Solution: | ||
| def wordBreak(self, s: str, wordDict: List[str]) -> bool: | ||
| # dp[index] = is_reached?(True | False) |
There was a problem hiding this comment.
dp という名前は好まれません。上から見ていって何か分からないので。
https://discord.com/channels/1084280443945353267/1247673286503039020/1254123432438927432
There was a problem hiding this comment.
ご指摘ありがとうございます。is_prefix_formatableに変更しました。(step4に記載)
| return False | ||
| ``` | ||
|
|
||
| ##### メモ化再帰で解き直す |
There was a problem hiding this comment.
確認しました。メモ化が公式で提供されているんですね。
時々は公式Docs見直さないとだめですね…
|
|
||
| ##### やったうえでの感想 | ||
|
|
||
| - DPのほうが見通しよいし、読みやすい |
There was a problem hiding this comment.
再帰の場合ももう少し整理すると見通しよく書けます。他の人のPRをもう少し読んでみると良いと思いました。
There was a problem hiding this comment.
直してみました。(step4に追記)
メモ化がfunctools.cacheに吸収されたこともあって、だいぶ見通しよくなりました。
| ```python3 | ||
| class Solution: | ||
| def wordBreak(self, s: str, wordDict: List[str]) -> bool: | ||
| start_index_to_match_lengths = dict() |
There was a problem hiding this comment.
手作業でやる場合このような処理はしない気がしていまして、DP の場合と同様に各 i に対して各 word が当てはまるかを確かめる方が直感的に思いました。
|
|
||
| def rec(index: int) -> bool: | ||
| if index in memo: | ||
| return False |
There was a problem hiding this comment.
変数名 memo だと return memo[index] であることを想定して読むと思います。
|
|
||
| if dp[len(s)]: | ||
| return True | ||
| return False |
There was a problem hiding this comment.
ありがとうございます。直接return するほうが見通しよいですね。配列のマイナスインデックスでこういうときに使うんですね。機能として知っていても有効に使えたことはありませんでしたので、勉強になります。
| return False | ||
|
|
||
| return _rec(0) | ||
| ``` |
There was a problem hiding this comment.
下のコードより上のコードの方が読みやすいです。
関数内関数は先頭に_をつけなくても良いと思います。
メソッドで外部から呼ばれることを想定しない場合は先頭に_をつける場合があります。
http://google.github.io/styleguide/pyguide.html#3162-naming-conventions
関数内関数なのでそこまでこだわりませんが、rec よりも良い名前がつけられると読みやすくなると思いました。
| if s.startswith(word, i): | ||
| is_prefix_formatable[i+len(word)] = True | ||
|
|
||
| return is_prefix_formatable[len(s)] |
There was a problem hiding this comment.
よいと思います。細かいですが関数名にあわせてformatableはbreakableとした方が分かりやすいと思いました。
| def _rec(index: int) -> bool: | ||
| if index == len(s): | ||
| return True | ||
| return any(s.startswith(word, index) and _rec(index + len(word)) for word in wordDict) |
There was a problem hiding this comment.
個人的にはif index > len(s): return falseを入れたほうが良いのではないかと思いました。
(s.startswith(word, index)がfalseだった段階で_rec(index + len(word)が評価されずにs.startswith(word, index) and _rec(index + len(word))がfalseであるという判断が下されるので大丈夫なんですが、一見するとindexがlen(s)を過ぎた後も延々と_recが呼び出されてしまうんじゃないかとちょっと不安になったので)
| class Solution: | ||
| def wordBreak(self, s: str, wordDict: List[str]) -> bool: | ||
| @functools.cache | ||
| def _rec(index: int) -> bool: |
There was a problem hiding this comment.
_rec という関数名ですが、内部実装を表す名前であって、個人的には関数内関数でもあまり使わないほうがよいかなと感じます。代替案としては word_break_helper (or *_auxiliary) などでしょうか。
| def _rec(index: int) -> bool: | ||
| if index == len(s): | ||
| return True | ||
| return any(s.startswith(word, index) and _rec(index + len(word)) for word in wordDict) |
There was a problem hiding this comment.
個人的に、内包表記よりも (上のように) ループとフラグを使って書きたくなります。Pythonic ではあるのですが、内包表記はもっと簡便な処理に使いたいです。
| ```python3 | ||
| class Solution: | ||
| def wordBreak(self, s: str, wordDict: List[str]) -> bool: | ||
| is_prefix_formatable = [False] * (len(s)+1) |
There was a problem hiding this comment.
スペースを空けるのが一般的かと思います。
is_prefix_formatable = [False] * (len(s) + 1)
is_prefix_formatable[i + len(word)]There was a problem hiding this comment.
PEP8 のおすすめは開けないんですが、どちらでもいいかと思います。
https://peps.python.org/pep-0008/#other-recommendations
Google Python はどちらでもよいです。Google C++ は開けるんだったかと思います。
There was a problem hiding this comment.
演算子の周りのスペースの話です。
https://google.github.io/styleguide/pyguide.html#36-whitespace
| ```python3 | ||
| class Solution: | ||
| def wordBreak(self, s: str, wordDict: List[str]) -> bool: | ||
| is_prefix_formatable = [False] * (len(s)+1) |
There was a problem hiding this comment.
英語のスペル的には formattable ですね。ただ別途コメントがある通り、私も breakable の方が良いと思います。format と言われると f-string のようなものを連想します。
| start_index_to_match_lengths[i] = set() | ||
| start_index_to_match_lengths[i].add(len(word)) | ||
|
|
||
| memo = dict() |
There was a problem hiding this comment.
dp 同様、memo よりも説明的な名前が良いと感じます。index_to_breakable などはどうでしょう。
| class Solution: | ||
| def wordBreak(self, s: str, wordDict: List[str]) -> bool: | ||
| @functools.cache | ||
| def _rec(index: int) -> bool: |
There was a problem hiding this comment.
inner fuction の関数名の先頭には _ は付けないことが多いと思います。 non-public なメソッドやインスタンス変数のほうには付けることが多いと思います。
参考までにスタイルガイドへのリンクを貼ります。
https://peps.python.org/pep-0008/#method-names-and-instance-variables
Use one leading underscore only for non-public methods and instance variables.
https://google.github.io/styleguide/pyguide.html#3162-naming-conventions
Prepending a single underscore (_) has some support for protecting module variables and functions (linters will flag protected member access).
上記のスタイルガイドは唯一絶対のルールではなく、複数あるスタイルガイドの一つに過ぎないということを念頭に置くことをお勧めします。また、所属するチームにより何が良いとされているかは変わります。自分の中で良い書き方の基準を持ちつつ、チームの平均的な書き方で書くことをお勧めいたします。
There was a problem hiding this comment.
関数内関数とnon-publicな関数の区別がついていませんでした。勉強になりました。
This problem:
https://leetcode.com/problems/word-break
Next problem:
https://leetcode.com/problems/linked-list-cycle/description/