アントライは、トライを実装する多くの異なる方法があることは本質的に正しいです。大規模でスケーラブルなトライの場合、ネストされた辞書は扱いにくくなる可能性があります-または少なくともスペース効率が悪くなります。しかし、あなたはまだ始まったばかりなので、それが最も簡単なアプローチだと思います。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をどのように構成するかによって、かなり複雑になる可能性があります。あなたはそれを正しくするためにレーベンシュタイン 距離についていくつかのことを学ぶ必要があるかもしれません。