問題
言葉をTrie を
insert(w)は、言葉 wをしまいます。 何も 返しません search(w)は、言葉 wがまる ごとしまって あれば 真、 無ければ 偽を返します startsWith(p)は、pで始まる 言葉が 1 つでもしまって あれば 真、 無ければ 偽を 返します。 pそのものが しまってある 場合も真です
メソッドのstarts_with のようにInsert、Search、StartsWith と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 の
アルゴリズムのチートシートの「ハッシュマップ」に、この型の要点を表とコードでまとめています。
まず 考える手順
段 1
まずはsearch はstartsWith は、
段 2
頭が"car" と "cart" は c、a、r まで"cart" だけが t の
段 3
節には、insert は、search と startsWith は、search はstartsWith は
段 4
たどるNone、null、nil) をsearch と startsWith は
解答
Python
Python
class TrieNode:
def __init__(self):
self.kids = {}
self.end = False
class Trie:
def __init__(self):
self.root = TrieNode()
def insert(self, w):
node = self.root
for ch in w:
kids = node.kids
nxt = kids.get(ch)
if nxt is None:
nxt = TrieNode()
kids[ch] = nxt
node = nxt
node.end = True
def walk(self, s):
node = self.root
for ch in s:
kids = node.kids
if ch not in kids:
return None
node = kids[ch]
return node
def search(self, w):
node = self.walk(w)
if node is None:
return False
return node.end
def starts_with(self, p):
node = self.walk(p)
return node is not NoneJavaScript
JavaScript
class TrieNode {
constructor() {
this.kids = new Map();
this.end = false;
}
}
class Trie {
constructor() {
this.root = new TrieNode();
}
insert(w) {
let node = this.root;
for (const ch of w) {
const kids = node.kids;
let next = kids.get(ch);
if (next === undefined) {
next = new TrieNode();
kids.set(ch, next);
}
node = next;
}
node.end = true;
}
walk(s) {
let node = this.root;
for (const ch of s) {
node = node.kids.get(ch);
if (node === undefined) {
return null;
}
}
return node;
}
search(w) {
const node = this.walk(w);
if (node === null) {
return false;
}
return node.end;
}
startsWith(p) {
const node = this.walk(p);
return node !== null;
}
}Go
Go
type trieNode struct {
kids [26]*trieNode
end bool
}
type Trie struct {
root *trieNode
}
func newTrie() *Trie {
return &Trie{&trieNode{}}
}
func (t *Trie) Insert(
w string,
) {
node := t.root
for _, ch := range w {
i := ch - 'a'
if node.kids[i] == nil {
node.kids[i] = &trieNode{}
}
node = node.kids[i]
}
node.end = true
}
func (t *Trie) walk(
s string,
) *trieNode {
node := t.root
for _, ch := range s {
node = node.kids[ch-'a']
if node == nil {
return nil
}
}
return node
}
func (t *Trie) Search(
w string,
) bool {
node := t.walk(w)
return node != nil && node.end
}
func (t *Trie) StartsWith(
p string,
) bool {
return t.walk(p) != nil
}Go はch - 'a' で、a を 0 番、z を 25 番に
計算量
- 言葉や
頭の 長さを L と すると、 どの 操作も 時間は O(L) です。 1 文字ごとに 子を 1 回引いて 進むだけで、 しまった 言葉の 数には 関係しません - 使う
記憶は、 しまった 言葉の 文字数の 合計を S として O(S) です。 頭が 重なる 言葉は 節を 分け合うので、 実際は これより 少なく済みます - Go の
書き方は、 節ごとに 26 個の 置き場を 必ず 持ちます。 文字の 種類が 多い ときは、 Python や JavaScript のように 表で 持つほうが 記憶を 節約できます
よく ある間違い
- 終わりの
印を 付け忘れる。 節が あるだけで 真を 返すと、 "cart"しかしまっていないのにsearch("car")が真に なります。 言葉の 終わりの 節にだけ印を 付け、 searchはそれを見ます startsWithでも印を 見てしまう。 逆に、 頭の 確かめで 印まで 見ると、 "cart"があるのに startsWith("car")が偽に なります。 頭は たどり 切れたか どうかだけで 決まります - 調べる
ときに 節を 作ってしまう。 searchの中で、 無い子を 作りながら 進むと、 調べただけで 木が 育ちます。 作るのは insertだけにし、調べる 側は 無ければ すぐ偽を返します
解答を動かした版 Python 3.12.14Node.js 22.23.4Go 1.24.6確認日 2026年10月30日
面接での 話し方の例
書き始める
「集合だけでは、
頭で 始まる 言葉が あるかを 調べるのに 全部の 言葉を 見る ことになります。 そこで、 1 文字ごとに 枝分かれする 木に 言葉を しまい、 言葉の 終わりの 節に 印を 付けます。 どの 操作も、 言葉の 長さの 分だけ子を たどるので O(L) です。」
書き終えたら、
「例 1 で
たどります。 "cart"をしまうと、 根から c、a、r、tの節が でき、 tに印が 付きます。 search("car")はrまでたどれますが 印が 無いので 偽、 startsWith("car")はたどり 切れたので 真です。 "car"を足すと rに印が 付き、 search("car")も真に なります。」
