Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
198 changes: 198 additions & 0 deletions 127WordLadder.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
# 進め方

Step1 : 問題を解く。

Step2 : 他の人のPRを参照し、コメントする。

Step3 : 3回続けてエラーが出ないように書く。ドキュメントを参照する。

# 実践

## Step1

### 思考ログ

動きません。

けっこう時間を費やしてしまったので、次回以降は解法がわからなければ解答をすぐ見る
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

発想の仕方はありますが、とはいえ、さっさと答えを見ていいと思います。
5分考えて手が止まったら、調べるくらいがいいように思っています。

だいたいコメント集からたどれます。
https://docs.google.com/document/d/11HV35ADPo9QxJOpJQ24FcZvtvioli770WWdZZDaLOfg/edit?tab=t.0


```python
class Solution:
def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
# まず入出力を合わせる
num_c = len(beginWord)

def is_one_diff(x, y):
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

こちらでも問題なさそうですがis_adjacentとか他の候補もありそうです!

diff_count = 0
for i in range(num_c):
if x[i] == y[i]:
continue
diff_count += 1
if diff_count > 1:
return False
return diff_count == 1

def calculate_min_length(target, used=set()):
if is_one_diff(target, endWord):
return 1
word_to_min = {}
for i, word in enumerate(wordList):
if word in used:
continue
if not is_one_diff(target, word):
continue
used.add(word)
length = calculate_min_length(word, used) + 1
if word not in word_to_min:
word_to_min[word] = length
else:
word_to_min[word] = min(word_to_min[word], length)

return min(word_to_min.values())

return calculate_min_length(beginWord)

```

最初スタックで書いてしまった。bfsが分かった気がする

```python
class Solution:
def ladderLength(self, begin_word: str, end_word: str, word_list: List[str]) -> int:
# 計算量は文字数×単語数の階乗
# 多すぎるきがする
num_c = len(begin_word)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

これすると word_list の中に長さが短い単語があると例外を投げそうですね。


def is_one_diff(x, y):
diff_count = 0
for i in range(num_c):
if x[i] == y[i]:
continue
diff_count += 1
if diff_count > 1:
return False
return diff_count == 1

used = set()
used.add(begin_word)
queue = deque([(1, begin_word)])


while queue:
distance, base = queue.popleft()

for word in word_list:
if word in used:
continue
if not is_one_diff(base, word):
continue
if word == end_word:
return distance + 1
used.add(word)
queue.append((distance + 1, word))

return 0

```

## Step2

### 同じ問題を解いた人のプルリクを見る

https://github.com/SuperHotDogCat/coding-interview/pull/45/files

- 時間計算量を考える
- https://github.com/SuperHotDogCat/coding-interview/pull/45/files#r1980741221

https://github.com/tshimosake/arai60/pull/11

- string、文字コードについて
- https://docs.python.org/ja/3.13/library/string.html
- https://github.com/t0hsumi/leetcode/pull/12#discussion_r1886759238

https://github.com/t0hsumi/leetcode/pull/20

- 見積もり、このくらい精度で見積もりたい
- https://github.com/t0hsumi/leetcode/pull/20/files#r1944383585
- 入出力に引っ張られないようにする
- https://github.com/t0hsumi/leetcode/pull/20/files#r1938166243
- ジェネレーター式
- 最大でword_listの分だけメモリを減らせる
- 今回はそこまで気にしなくてもよいと思っている。set(word_list)を使うので、メモリの消費量は半分程度くらいにしかならない。
- https://docs.python.org/ja/3.11/reference/expressions.html#generator-expressions

https://github.com/olsen-blue/Arai60/pull/20

- 分かりやすい
- wordListを消していけばよいのか

### 書き直す

関数に詰め込みすぎている感じがある

```python
class Solution:
def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
num_word = len(beginWord)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

num_word ですと、単語の数という意味に感じます。 word_length のほうが直感的だと思います。

word_queue = deque([(beginWord, 1)])
word_set = set(wordList)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

自分は変数名には型を表す単語は含めないことが多いのです。ただ、今回は wordList が list 型で、それに対して word_set と定義しているため、悩ましいところです。


def search_end_word(word: str, step: int) -> Optional[int]:
for i in range(num_word):
for c in string.ascii_lowercase:
target = f'{word[:i]}{c}{word[i+1:]}'
if target not in word_set:
continue
if target == endWord:
return step + 1
word_queue.append((target, step + 1))
word_set.remove(target)
return None

while word_queue:
word, step = word_queue.popleft()
result = search_end_word(word, step)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

search_end_word() の中で word_queue に要素が追加されるという流れが読み取りづらいように感じました。この程度の分量であれば、インラインで書いてしまってもよいと思います。

if result is not None:
return result

return 0
```

## Step3

### 3回連続で再現できるようになる

13分

18分 wordsとwordを書き間違えた。スコープが長いときは使わないほうが良いかも

7分

```python
class Solution:
def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
usable = set(wordList)

def get_next_words(word):
next_words = []
for i in range(len(word)):
for c in string.ascii_lowercase:
candidate = f'{word[:i]}{c}{word[i+1:]}'
if candidate not in usable:
continue
next_words.append(candidate)
usable.remove(candidate)
return next_words

queue = deque([(beginWord, 1)])
while queue:
word, step = queue.popleft()
next_words = get_next_words(word)
next_step = step + 1
for target in next_words:
if target == endWord:
return next_step
queue.append((target, next_step))
Comment on lines +187 to +195
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

好みですが、get_next_words()は階層的(あるwordに対して、次の遷移可能な語を全て出している)な一方で、queueの方はiterativeに出し入れしていることが違和感に感じました。
ぼくなら、ネストが深くなりますがfor next_word in get_next_words(word)としてiterativeに書いていくか、下のようにqueueをあるステップで遷移可能な語でまとめ上げるかなと思います。

        words = [beginWord]
        num_transformations = 1
        while words:
            next_words = []
            for word in words:
                if word == endWord:
                    return num_transformations
                next_words.extend(get_next_words(word))
            words = next_words
            num_transformations += 1

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

その視点はありませんでした。ありがとうございます。

return 0

```