トライを実装するにはさまざまな方法があるという点で、アンワインドは本質的に正しいです。また、大規模でスケーラブルなトライの場合、ネストされた辞書は扱いにくくなるか、少なくともスペース効率が低下する可能性があります。しかし、あなたは始めたばかりなので、それが最も簡単なアプローチだと思います。trie
ほんの数行で簡単にコーディングできます。まず、トライを構築する関数:
>>> _end = '_end_'
>>>
>>> def make_trie(*words):
... root = dict()
... for word in words:
... current_dict = root
... for letter in word:
... current_dict = current_dict.setdefault(letter, {})
... current_dict[_end] = _end
... return root
...
>>> make_trie('foo', 'bar', 'baz', 'barz')
{'b': {'a': {'r': {'_end_': '_end_', 'z': {'_end_': '_end_'}},
'z': {'_end_': '_end_'}}},
'f': {'o': {'o': {'_end_': '_end_'}}}}
に慣れていない場合はsetdefault
、辞書でキーを検索するだけです (ここでは、letter
または_end
)。キーが存在する場合は、関連付けられた値を返します。そうでない場合は、そのキーにデフォルト値を割り当て、値 ({}
または_end
) を返します。get
(辞書も更新するバージョンのようなものです。)
次に、単語がトライに含まれているかどうかをテストする関数:
>>> def in_trie(trie, word):
... current_dict = trie
... for letter in word:
... if letter not in current_dict:
... return False
... current_dict = current_dict[letter]
... return _end in current_dict
...
>>> in_trie(make_trie('foo', 'bar', 'baz', 'barz'), 'baz')
True
>>> in_trie(make_trie('foo', 'bar', 'baz', 'barz'), 'barz')
True
>>> in_trie(make_trie('foo', 'bar', 'baz', 'barz'), 'barzz')
False
>>> in_trie(make_trie('foo', 'bar', 'baz', 'barz'), 'bart')
False
>>> in_trie(make_trie('foo', 'bar', 'baz', 'barz'), 'ba')
False
挿入と削除は練習問題としてお任せします。
もちろん、Unwind の提案はそれほど難しいものではありません。正しいサブノードを見つけるには線形検索が必要になるため、速度が若干低下する可能性があります。ただし、検索は可能な文字数に制限されます。これを含めると 27 文字になります_end
。また、ノードの膨大なリストを作成し、彼が提案するようにインデックスでそれらにアクセスしても、何も得られません。リストをネストすることもできます。
最後に、有向非巡回単語グラフ (DAWG) の作成はもう少し複雑になることを付け加えておきます。これは、現在の単語が構造内の別の単語と接尾辞を共有している状況を検出する必要があるためです。実際、これは、DAWG をどのように構造化するかによって、かなり複雑になる可能性があります。正しく理解するには、レーベンシュタイン 距離について学ぶ必要があるかもしれません。