トライ木 (Trie) で言葉の頭を調べる仕組みを作る

言葉を​足す、​言葉が​まるごと​あるか​調べる、​ある​頭で​始まる​言葉が​あるか​調べる、の​ 3 つの​操作を​持つ仕組みを​作る。​1 文字ずつ​枝分かれする​木に​言葉を​しまう​練習です。

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

問題

言葉をしまっておき、あとから調べられる仕組み Trie を作ってください。次の 3 つの操作を持ちます。

  • insert(w) は、言葉 w をしまいます。何も返しません
  • search(w) は、言葉 w がまるごとしまってあれば真、無ければ偽を返します
  • startsWith(p) は、p で始まる言葉が 1 つでもしまってあれば真、無ければ偽を返します。p そのものがしまってある場合も真です

メソッドの名前は、JavaScript は上のまま、Python は starts_with のように小文字と下線、Go は Insert、Search、StartsWith と頭を大文字にします。Go では newTrie() で作ります。

  • 言葉と頭はどれも英小文字 (a から z) だけで、長さは 1 以上 2000 以下です
  • 操作は全部で 30000 回以下です

例は、呼ぶ操作の名前の並び ops と、それぞれに渡す引数の並び args で書きます。最初の "Trie" は作るところです。出力は操作ごとの戻り値の並びで、作るところと insert は null です。

入力と出力の例

例 1

"cart" をしまったあとでは、"car" は頭としてはありますが、言葉としてはまだありません。"car" を足すと、言葉としても見つかります。

入力

ops = ["Trie", "insert",
  "search", "search",
  "startsWith", "insert",
  "search"]
args = [[], ["cart"],
  ["car"], ["cart"],
  ["car"], ["car"],
  ["car"]]

出力

[null, null, false, true,
 true, null, true]

例 2

頭が途中まで合っていても、次の文字の枝が無ければ偽です。

入力

ops = ["Trie", "insert",
  "insert", "search",
  "startsWith",
  "startsWith", "search"]
args = [[], ["sun"],
  ["sunday"], ["sund"],
  ["sund"], ["sat"],
  ["sunday"]]

出力

[null, null, null, false,
 true, false, true]

例 3

何もしまっていないうちは、どれを調べても偽です。

入力

ops = ["Trie", "search",
  "startsWith", "insert",
  "search", "startsWith"]
args = [[], ["a"], ["a"],
  ["a"], ["a"], ["a"]]

出力

[null, false, false, null,
 true, true]

面接での聞かれ方

面接では、入力補完や辞書の検索を例に出して渡されることが多いです。

  • 「検索窓に打った頭から、候補があるかをすぐ答える仕組みを作ってください」
  • 書き終えたあとに「言葉の集合 (set) を持つだけではだめですか?」「頭で始まる言葉を全部返すにはどうしますか?」と続けて聞かれます
  • 「文字の種類が英小文字だけでなく、日本語も入るならどう持ちますか?」と、子の持ち方を聞かれることもあります

search と startsWith の違いは、最後の節に「ここで終わる言葉がある」という印があるかどうかだけです。書き始める前に、この 2 つの違いを口で確かめておくと迷いません。

アルゴリズムのチートシートの「ハッシュマップ」に、​この​型の​要点を​表と​コードで​まとめています。

main.py
class Trie:
    def __init__(self):
        # ここに持つ物を書きます
        pass

    def insert(self, w):
        # ここに書きます
        pass

    def walk(self, s):
        # ここに書きます
        pass

    def search(self, w):
        # ここに書きます
        pass

    def starts_with(self, p):
        # ここに書きます
        pass


# 「実行」を押すと、下の行が動きます
obj = Trie()
print(obj.insert("cart"))

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