-
Notifications
You must be signed in to change notification settings - Fork 0
127. Word Ladder #36
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?
127. Word Ladder #36
Conversation
A transformation sequence from word beginWord to word endWord using a dictionary wordList is a sequence of words beginWord -> s1 -> s2 -> ... -> sk such that: Every adjacent pair of words differs by a single letter. Every si for 1 <= i <= k is in wordList. Note that beginWord does not need to be in wordList. sk == endWord Given two words, beginWord and endWord, and a dictionary wordList, return the number of words in the shortest transformation sequence from beginWord to endWord, or 0 if no such sequence exists.
| continue | ||
| yield candidate + right_half | ||
|
|
||
| checked = {} |
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.
消し忘れですか?
| checked = {} | ||
| left_half_to_right, right_half_to_left = self.collect_half_same_words(wordList) | ||
| words = [beginWord] | ||
| transfered_words_count_so_far = 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.
transferredですか?traverseの書き間違えのような気もしますが…ladder_lengthとかでいかがですか?
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.
ladder「ハシゴ」という意味なんですね。lengthと合わせるとスッキリします。
この辺思いつく単語力があまりないので参考になります
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.
問題タイトルと関数名でladderという単語が使われていたのでここでは適切かと思いますが、そうでなければ、いきなりladder lengthと言われてもなんのことか分からないかと…
| while True: | ||
| if not current_words: |
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.
current_words の中身があることの確認を while 句の条件判定に入れたい気持ちになりました
| yield candidate_word | ||
|
|
||
| current_words = [beginWord] | ||
| num_words_so_far = 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.
_so_far の部分は無くても意味がわかる (処理が終了するまでは "ここまでの" 値であると推測できる) ので不要かと思いました。
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.
これは確かにそうですね。こだわりすぎてました。ありがとうございます
|
|
||
| current_words = [beginWord] | ||
| num_words_so_far = 0 | ||
| seen = set() |
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.
何が seen なのかが名前に反映されていた方が親切に思いました。 seen_words みたいな。
| if not current_words: | ||
| return 0 | ||
| next_words = [] | ||
| num_words_so_far += 1 |
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.
好みですが num_words_so_far++ も良いと思います
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.
Pythonはインクレメント演算子ないんですよね…
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.
あら、そうだったのですね。失礼しました🙏
| seen.add(candidate_word) | ||
| yield candidate_word | ||
|
|
||
| current_words = [beginWord] |
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.
current_ の部分は無くても問題ないかなと思いました
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.
文字列パターン (例: d?g, ?og) とパターンに当てはまる文字列リストのマップ (python だと dictionary ですかね) を用いる解法もあるので、実装してみても良いかもしれません。
| class Node: | ||
| def __init__(self, val): | ||
| self.val = val | ||
| self.next = [] |
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.
文字列をインデックスにマッピングし、 next に入れると、文字列処理の回数が減り、少し軽くなるかもしれません。
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.
ありがとうございます、なるほど発想になかったです。
試してみます
| visited.add(adjacent_word) | ||
| ``` | ||
|
|
||
| - 素早くhamming distanceを求める謎のアルゴリズム(https://cs.stackexchange.com/questions/93467/data-structure-or-algorithm-for-quickly-finding-differences-between-strings) |
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.
面白いアルゴリズムですね。初めて知りました。ソフトウェアエンジニアの常識には含まれていないと思います。
|
|
||
| def generate_adjacent_words(word, candidates): | ||
| for candidate_word in candidates: | ||
| if candidate_word in seen: |
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.
個人的には、関数で参照している関数スコープの外のオブジェクトは関数定義よりも前に出します (この例ならgenerate_adjacent_words()の定義よりも前にseenを作っておきます)。
理由は頭から読んでいったときに関数内で使用している定義されていないものを見てなんだコレとなって探す作業が発生するのを避けるためと、仮にseenみたいなオブジェクトが作られるより前にgenerate_adjacent_words()が呼ばれたらNameErrorが発生しますがそれが嫌だからです。
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.
これ確かにになりました。ためになります。ありがとうございます
| - 以下のはCPythonに従った。なぜCPythonがそういう実装なのかはよくわからない。 | ||
| - PREV, NEXTなどへの参照を配列で実装していた | ||
| - 最大の保存長を超えたとき、なぜかrootに新しい要素を入れて、root.nextを新たなrootにしていた | ||
| - lru_cache_wrapperは、「ユーザー入力の引数を入れた、cache処理をしているuser_functionのwrapper」を返す関数のはずだが、なぜ@lru_cahceを使った関数を何回も呼ぶときに、lru_cache_wrapperが複数回呼ばれず、user_function_wrapperが複数回呼ばれるかが理解できない、、、 |
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.
なぜ@lru_cahceを使った関数を何回も呼ぶときに、lru_cache_wrapperが複数回呼ばれず、user_function_wrapperが複数回呼ばれるかが理解できない
lru_cache_wrapper が呼ばれるのは @lru_cache が実行されたタイミングの1回だけで、「複数回呼ばれる」と書いているのは呼んでる対象がuser_function_wrapperそのものだからじゃないでしょうか?
@lru_cache(128)
def f(a: int):
print(a)みたいなコードを実行したときに呼ばれるのがlru_cache_wrapperで、 f(2) とかのコードを実行されたときに呼ばれるのがuser_function_wrapper
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.
コメントまで見てくださり大変ありがとうございます🙇♀️
今時間かけて考えてみたのですが、「関数を呼び出す関数」の理解が甘かったです。外側の関数を呼び出しただけでは、内側は定義されるだけで、さらにその後実行しないと呼び出されないですね。
今回の場合、
1, @lru_cache(128)まで書いた段階ではdecorate_user_functionは返り値として定義されるだけで呼ばれていない
2, その下にdef f(a: int): print(a)と書いて、fが引数となり、decorate_user_functionと、lru_cache_wrapperが呼ばれる。user_funtion_wrapperは返り値として定義されるだけでまだ呼ばれない
3, f(2)と書く段階で、user_funtion_wrapperが2を引数として呼ばれる
ですね。
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.
「書いた段階」とか「書いて」がどういう意味か分かってないですが、その行まで実行されたときだとすると、1〜3はそれぞれ
- lru_cacheが定義された状態でも@lru_cache(128)だけ実行したらSyntaxErrorなる
- lru_cacheが呼ばれて、decorate_user_functionという関数オブジェクトが作られてそれが返る。decorate_user_functionも実行され、その引数はfという関数オブジェクト。返り値は
lru_cache_wrapper(f, 128)の返り値で、それはuser_function_wrapperという関数オブジェクト。これがfという関数名で呼び出せるようになる。 - 同じ
という理解を自分はしています。2のように考えているのは https://docs.python.org/3/glossary.html#term-decorator の記述から、
@lru_cache(128)
def f(a: int):
print(a)が
def f(a: int):
print(a)
f = lru_cache(128)(f)とほぼ同じという理解をしているからです。
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://discord.com/channels/1084280443945353267/1200089668901937312/1215904902102913114)議論、いまいち理解できたか自信ないけどこういう実装のことかな | ||
| - word1→word2とたどる場合と、word2→ word1の場合があるので、それを同じと捉えればハミング距離の計算が半分になる |
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.
word1とword2が隣同士だと既に分かっていた場合、word2とword1は隣同士であることは自明なので、わざわざ再計算しないようにする、ということですよね。
| for word in words: | ||
| for adjacent_word in generate_adjacent_words(word): | ||
| adjacent[word].append((1, adjacent_word)) | ||
| word_and_num_so_far = [(1, beginWord)] |
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.
細かいですが、自分が書くときは、変数名の順序とリストの要素の中身の順序を合わせると思います。
| self._root[:] = [self._root, self._root, None, None] | ||
| self.len = 0 | ||
|
|
||
| def get_link(self, args): |
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.
argsという変数名に違和感があります。
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.
これ上のlru_cacheにつられてしまいましたが、確かに変ですね。
ありがとうございます。
| def generate_adjacent_words(word): | ||
| for i in range(len(word)): | ||
| for c in ascii_lowercase: | ||
| if c == word[i]: | ||
| continue | ||
| one_diff_word = word[:i] + c + word[i + 1:] | ||
| if one_diff_word not in wordList: | ||
| continue | ||
| yield one_diff_word |
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.
ちょっとネストが深いという気持ちになりました。
あと、wordList の中で、word に隣接している単語を generate するというのはちょっと推測しにくいです。
wordList を陽に引数にして ladderLength と並べるのは一つです。
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.
確かに呼ぶ側になって考えると、generate_adjacent_wordsという関数にwordを入れて、返ってくるのがwordListの中にあるものとなると変な感じしますね。not in wordList: continueの実装も奥まってますし。
引数にするのは案になかったです。ありがとうございます。
URL:https://leetcode.com/problems/word-ladder/description/
A transformation sequence from word beginWord to word endWord using a dictionary wordList is a sequence of words beginWord -> s1 -> s2 -> ... -> sk such that:
Every adjacent pair of words differs by a single letter. Every si for 1 <= i <= k is in wordList. Note that beginWord does not need to be in wordList. sk == endWord
Given two words, beginWord and endWord, and a dictionary wordList, return the number of words in the shortest transformation sequence from beginWord to endWord, or 0 if no such sequence exists.