幅優先探索 (BFS) で言葉の梯子のいちばん短い長さを求める

1 文字ずつ​置き換えて、​始めの​言葉から​終わりの​言葉まで、​言葉の​一覧に​ある​言葉だけを​通って​渡る。​1 文字を​伏せた形で​言葉を​箱に​分けて​おき、​幅優先で​近い​言葉から​広げます。

海外で定番の問題リスト 4 本のうち 3 本に入っています (NeetCode 150、Grind 75、LeetCode Top Interview 150)

問題

始めの言葉 begin、終わりの言葉 goal、言葉の一覧 words を受け取ります。begin から始めて、1 回に 1 文字だけ置き換えた言葉へ渡っていき、goal に着きたいとします。渡った先の言葉は、どれも words に入っていなければなりません。いちばん短い渡り方で通る言葉の数 (begin と goal も数えます) を返す関数を書いてください。

  • 着けないときは 0 を返します。goal が words に無いときも 0 です
  • begin は words に入っていなくてもかまいません。begin と goal は違う言葉です
  • 言葉はどれも英小文字 (a から z) だけで、長さはみな同じ、1 以上 10 以下です
  • words の長さは 1 以上 5000 以下で、同じ言葉は 2 回出てきません

入力と出力の例

例 1

cold、cord、card、ward、warm と渡ると、5 つの言葉で着きます。4 文字が全部違うので、これより短くはなりません。

入力

begin = "cold"
goal = "warm"
words = ["cord", "card",
  "ward", "warm",
  "word", "worm"]

出力

5

例 2

pen から pin へは 1 文字の違いですが、pin が一覧に無いので渡れません。

入力

begin = "pen"
goal = "pin"
words = ["pan", "pun"]

出力

0

例 3

lake、like、lime と 3 つで着きます。bake や lane へも渡れますが、遠回りです。

入力

begin = "lake"
goal = "lime"
words = ["bake", "lane",
  "like", "lime"]

出力

3

面接での聞かれ方

面接では、言葉遊びの形のまま渡されることが多く、グラフの問題だと気づけるかが最初の山です。

  • 「1 文字ずつ変えて、ある言葉から別の言葉まで最短何手で行けますか」
  • 書き終えたあとに「隣の言葉を見つける手間はどれくらいですか?」「一覧が 5000 語でも間に合いますか?」と、隣を探す部分の計算量を詰めて聞かれます
  • 「両端から同時に広げると、どれくらい速くなりますか?」「実際の渡り方も返せますか?」と、さらに広げて聞かれることもあります

数えるのが「置き換えの回数」なのか「通る言葉の数」なのかで、答えが 1 つずれます。例 1 で 5 になることを、書く前に面接官と確かめておきます。

アルゴリズムのチートシートの「グラフ探索と幅優先探索」に、​この​型の​要点を​表と​コードで​まとめています。

main.py
def ladder_length(begin, goal, words):
    # ここに書きます
    pass


# 「実行」を押すと、この行の結果が出ます
print(ladder_length("cold", "warm", ["cord", "card", "ward", "warm", "word", "worm"]))

できているか「例で確かめる」を押すと、問題の例を全部この関数に渡して、通ったかをここに出します