Skip to content

Conversation

@SuperHotDogCat
Copy link
Owner

Comment on lines +6 to +11
def is_connectable(s1: str, s2: str) -> bool:
count = 0
for i in range(len(s1)):
if s1[i] != s2[i]:
count += 1
return count == 1
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

countが1を超えた段階でFalseを返すと、少しだけ早くできそうです。

Comment on lines +92 to +97
tail_word, sequence_length = queue.popleft()
for key in word_to_keys[tail_word]:
for word in key_to_words[key]:
if word not in seen:
if word == endWord:
return sequence_length + 1
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ここのネストがかなり深くなっていくので、tail_wordを引数として、次のwordを生成する関数があってもいいのではと感じました。以下みたいな感じでしょうか?

def generate_next_word(word: str, seen: set[str]) -> Iterator[str]:
    for key in word_to_keys[word]:
        for next_word in key_to_words[key]:
            if next_word in seen:
                continue
            yield next_word

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

同意見です。(ちょっと驚きました。)

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wordがendWordの時すぐreturnすればネストを一つ消せることに気づきましたが, それでもまだ長い気がするのでyieldを使うことにします。

while queue:
    tail_word, sequence_length = queue.popleft()
    for key in word_to_keys[tail_word]:
        for word in key_to_words[key]:
            if word == endWord:
                return sequence_length + 1
            if word not in seen:
                queue.append((word, sequence_length + 1))
                seen.add(word)

def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
wordList = set(wordList)
queue = deque([[beginWord, 1]])
lower_characters = 'abcdefghijklmnopqrstuvwxyz'
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

string.ascii_lowercaseを使うのもありかと思います。字数はそれほど変わりませんが、タイプミスや入れ忘れは減ると思います。

queue.append((word, sequence_length + 1))
seen.add(word)
return 0
# 書いてから思ったけど1に等しいかどうかで判断する方が綺麗かも
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

僕もそう思います

class Solution:
def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
for word in wordList:
if word == endWord: # ここ, if endWord not in wordListで効率化できるな
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pythonのリストのin, not inについてはネイティブコードで実装されているので、速度的にも入力文字数という意味でもnot inとした方が良さそうです。
https://github.com/python/cpython/blob/main/Objects/listobject.c#L616

Comment on lines +73 to +74
key_to_words = defaultdict(list)
word_to_keys = defaultdict(list)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

両方とも用意するのわかりやすくていいですね

word_diff_count = 0
for i in range(len(s1)):
if s1[i] != s2[i]:
word_diff_count += 1
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

この問題で必要なのって、単語が隣接しているかどうかなので、diff_countについて2以上を数える必要がなくなり、そのようなロジックを組み込めるとそのぶんシンプルにできそうです。

if tail_word == endWord:
return sequence_length
for word in available_words:
if calc_word_diff(tail_word, word) == 1:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ネストが深すぎるので、ここを反転させてearly continueみたいな感じでやれると読みやすくできそうです。

@@ -0,0 +1,69 @@
# 単純にBFSをしてtime limit overしたもの, そりゃそうだろうなという感じの時間計算量
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

時間計算量を求めることはできますか?また、そこからおおよその実行時間を推定できますか?

Copy link
Owner Author

@SuperHotDogCat SuperHotDogCat Mar 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

この解答だとqueue.append((word, next_available_words, sequence_length + 1))で使える単語も保持していて遠回りも含めた全部のルートを考えてしまっているので, 大雑把には最悪の場合n = wordList.lengthとした時n×(n-1)×(n-2)×...×1となってしまい, O(n!)かかります。Pythonは1秒10^6stepほどだった気がするので今回wordList.length <=5000であり, スターリング公式でn!≒n^(n+1/2) / e^nとして考えるとn=5000を代入して1831^5000 × 70 / 10^6≒(2000)^5000×70/10^6=7×10^16495 sec程度かかる気がします。(2^10≒10^3を用いて計算)

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

あ、いやcalc_word_diffにword.length分かかるので今回word.length<=10なので7×10^16496 secでしょうか

Copy link

@nodchip nodchip Mar 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

n×(n-1)×(n-2)×...×1となってしまい

ここの部分に違和感を感じました。単語 S1 → S2 → S3 → ... → Sn と遷移して endWord にたどり着く場合、 endWord に近づく遷移と遠ざかる遷移があります。遷移は n 回行われますので、多めに見積もって 2^n に比例した個数の状態が保持されます。また wordList のコピーを毎回作っていますので、 2^n * n になると思います。 n = 5000 を代入すると、 log_{10}2 = 0.3 として、 2^5000 * 5000 ≒ 10^1500 * 5000 = 5.0 * 10^1503 くらいになると思います。
単語 S1 → S2 → S3 → ... → Sn と遷移して endWord にたどり着くような入力を意図して作れるかは未検討です。

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

こういう感じでしょうか
IMG_9372

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

はい、そのようにイメージしました。

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ちょっと実験してみました。
begin: hot, wordList: [lot, got, cot, cog], end: cogで少し樹形図書いてみたら9ルート出てきて確かに2^4のほうが4!よりは抑えられている気がしました。ありがとうございます。

word_diff_count += 1
return word_diff_count

word_graph = defaultdict(list)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

グラフの中に入れる要素を単語本体ではなく wordList 内のインデックスとすることで、処理を少し軽くすることができるかもしれません。

key = (word[:i], word[i + 1:])
key_to_words[key].append(word)
word_to_keys[word].append(key)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

私は初期化の部分で関数を分けるかもしれません。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants