トライ木と 深さ優先探索で 文字の 盤面から 言葉を全部探す
文字の
海外で定番の問題リスト 4 本には入っていません
問題
英小文字のboard と、words をwords の
boardは文字列の 並びで、 1 つの 文字列が 盤面の 1 行です。 board[r][c]が r 行 c 列のマスの文字です - 言葉は、
ある マスから 始めて、 上下左右に 隣り合う マスへ 1 つずつ 進み、 通った マスの 文字を 順に 読んで 作ります。 斜めには 進めません - 1 つの
言葉の 中で、 同じ マスを 2 回使う ことは できません wordsの中に 同じ 言葉は 2 回出てきません。 見つかった 言葉は、 作り方が 何通り あっても 1 回だけ 答えに入れます - 1 つも
作れなければ、 空の 並び []を返します - 盤面は 1 行以上 12 行以下、
1 列以上 12 列以下です。 wordsは 1 個以上 3000 個以下で、どの 言葉も 1 字以上 10 字以下の 英小文字です
入力と出力の例
例 1
文字の
例 2
2 行 3 列の
例 3
aba は、
面接での聞かれ方
トライ木と
- 「盤面から、
一覧に ある 言葉を 全部探してください」 - 言葉を 1 つだけ探す問題 (盤面の
単語探し) を 解いた あとの 続きとして 出される ことが よく あります。 言葉ごとに 盤面を 探すと、 言葉の 数だけ 時間が かかります - 「見つけた
言葉の 枝を トライ木から 外すと、 何が 速くなりますか?」と、 枝を 刈る 工夫を 聞かれる ことがあります
シニアの
ヒント
まだ開いていません
付箋は 4 枚。下の段ほど答えに近づきます
ヒント 2
ヒント 3
ヒント 4答えに近い
アルゴリズムのチートシートの「ハッシュマップ」に、
関わる学び
ハッシュは、
まず 考える手順
段 1
まずは
段 2
言葉を
段 3
盤面の
段 4
進むときは
解答
Python
Python
def word_search_grid(
board, words
):
root = {}
for w in words:
node = root
for ch in w:
if ch not in node:
node[ch] = {}
node = node[ch]
node["$"] = w
rows = len(board)
cols = len(board[0])
seen = set()
found = []
def dfs(r, c, node):
if not 0 <= r < rows:
return
if not 0 <= c < cols:
return
if (r, c) in seen:
return
ch = board[r][c]
node = node.get(ch)
if node is None:
return
if "$" in node:
word = node.pop("$")
found.append(word)
seen.add((r, c))
dfs(r + 1, c, node)
dfs(r - 1, c, node)
dfs(r, c + 1, node)
dfs(r, c - 1, node)
seen.remove((r, c))
for r in range(rows):
for c in range(cols):
dfs(r, c, root)
return sorted(found)JavaScript
JavaScript
function wordSearchGrid(
board, words
) {
const root = new Map();
for (const w of words) {
let node = root;
for (const ch of w) {
if (!node.has(ch)) {
node.set(ch, new Map());
}
node = node.get(ch);
}
node.set("$", w);
}
const rows = board.length;
const cols = board[0].length;
const seen = board.map(
() => Array(cols)
.fill(false)
);
const found = [];
const dfs = (r, c, node) => {
if (r < 0 || c < 0
|| r >= rows || c >= cols
|| seen[r][c]) {
return;
}
const next =
node.get(board[r][c]);
if (next === undefined) {
return;
}
if (next.has("$")) {
found.push(next.get("$"));
next.delete("$");
}
seen[r][c] = true;
dfs(r + 1, c, next);
dfs(r - 1, c, next);
dfs(r, c + 1, next);
dfs(r, c - 1, next);
seen[r][c] = false;
};
board.forEach((row, r) => {
[...row].forEach((_, c) => {
dfs(r, c, root);
});
});
return found.sort();
}Go
Go
import "sort"
type tnode struct {
next [26]*tnode
word string
}
func wordSearchGrid(
board []string,
words []string,
) []string {
root := &tnode{}
for _, w := range words {
n := root
for _, ch := range w {
i := ch - 'a'
if n.next[i] == nil {
n.next[i] = &tnode{}
}
n = n.next[i]
}
n.word = w
}
rows := len(board)
cols := len(board[0])
seen := make([][]bool, rows)
for r := range seen {
seen[r] = make([]bool, cols)
}
found := []string{}
var dfs func(int, int, *tnode)
dfs = func(
r, c int, n *tnode,
) {
if r < 0 || c < 0 ||
r >= rows || c >= cols ||
seen[r][c] {
return
}
n = n.next[board[r][c]-'a']
if n == nil {
return
}
if n.word != "" {
found = append(
found, n.word)
n.word = ""
}
seen[r][c] = true
dfs(r+1, c, n)
dfs(r-1, c, n)
dfs(r, c+1, n)
dfs(r, c-1, n)
seen[r][c] = false
}
for r := 0; r < rows; r++ {
for c := 0; c < cols; c++ {
dfs(r, c, root)
}
}
sort.Strings(found)
return found
}Python と JavaScript の$ の$ と
計算量
- 時間は、
いちばん悪いと O(m × n × 4 × 3^(L−1)) です。 m × n は マスの 数、 L は いちばん長い 言葉の 字数で、 1 つの マスから、 来た方向を 除く 3 方向へ L 字まで 広がります。 実際は トライ木に 無い 文字で 早く 戻るので、 ずっと 少なくなります - トライ木を
作るのに、 言葉の 字数の 合計 S に 比例する 時間と 記憶を 使います。 たどる 所の 記憶は、 使った 印の O(m × n) と、 再帰の 深さの O(L) です
よく ある間違い
- 言葉ごとに
盤面を探す。 動きますが、 言葉が 多いと 同じ 盤面を 何度も たどります。 頭が 同じ 言葉も まとめて 調べられる トライ木を使います - 戻る
ときに 印を 外し忘れる。 1 つの 道で 使った マスが、 別の 道で 使えなくなり、 例 1 の tone のような 言葉を 見落とします - 見つけた
言葉を 節から 外さない。 同じ 言葉を 別の 道で もう 一度見つけると、 答えに 2 回入ります。 外すか、 集合で 重なりを除きます - 言葉の
終わりで 戻ってしまう。 car を 見つけた ところで 止めると、 card のように car で 始まる 長い 言葉を 見落とします。 答えに 足した あとも、 続けて進みます
解答を動かした版 Python 3.14.7Node.js 24.22.1Go 1.24.2確認日 2026年11月8日
面接での 話し方の例
書き始める
「言葉を
全部 トライ木に 入れ、 盤面の 各マスから 深さ優先探索を トライ木の 根と 一緒に 進めます。 マスの 文字の 子が トライ木に 無ければ 戻り、 言葉の 終わりの 節に 来たら 答えに 足して 節から 外します。 使った マスには 印を 付け、 戻る ときに 外します。 最後に 辞書の 順に 並べます。 言葉ごとに 探すより、 盤面を 1 回た どるだけで 全部を 調べられます。」
書き終えたら、
「例 1 で
左上の c から 始めると、 トライ木の c の 下に a が あるので 右の a へ 進みます。 a の 下には t と r が あり、 右の t で cat、 下の r で car を 見つけて 答えに 足します。 r から さらに 下の o へ 進もうとしても、 car の 下に o は 無いので 戻ります。 rot は、 r の マスから 始めた ときに 見つかります。 全部の マスから 始め終えたら 並べ替えて、 car、 cat、 pet、 rot、 step、 tone です。」