Trie, или бор

Про сложение строк

Часто сложение строк называют конкатенацией, и рассматривают как бинарную математическую операцию.

Эта операция (далее +) обладает интересными свойствами.

  • во-первых, она ассоциативна: abc+(de+fg)=(abc+de)+fg=abcdefg
  • во-вторых, она НЕ коммутативна: abc+defdef+abc
  • в третьих, у этой операции, как и у многих други бинарных операций, есть нейтральный элемент, ε.
    • конкатенация интуитивно "похожа" на сложение больше, чем на умножение, а ε - на ноль, чем на единицу. Поэтому ε скорее назовут нулем для операции конкатенации.
    • поскольку ε+def не гарантированно равно def+ε, можно было бы сказать, что существует два нейтральных элемента: εl,εr - но это не так, посокльку εl=εl+εr=εr
  • без ε множество строк над алфавитом Σ с операцией конкатенации является полугруппой, а с ε - моноидом.

Конкатенация логично распространяется так же и на структуры данных, являющиеся последовательностями, такие, как списки, массивы.

Их также можно отнести к моноидам.

Trie

Trie - структура, которая (довольно неожиданно) уже встречалась нам в контексте сортировки послеовательнсотей за линейное врем. Она использовалась в одной из модификаций Radix Sort - это довольно экзотическое применение trie.

кстати, одно из названий trie - radix trie

Само название trie получилось "урезанием" слова retrieval и намекает на то, для чего используется структура.

Зачастую trie используют как ассоциативный массив для хранения информации

Если посмотреть на листья, в trie компактно упакованы следующие строки:

  • TWI, TWE
  • TZA, TZI
  • TALL, TALK
  • US
  • UNI

А если добавить узлы, не являющиеся листьями -

  • ε
  • T, U
  • TW, TZ, TA, UN
  • TAL

Использование

За O(m), где m - глубина trie, можно получить информацию о том, содержится ли в нем строка, или нет.

  • в общем, trie можно использовать для хранения данных
  • также используется в spell checker'ах, программах для автодополнения текста
  • а еще trie применяется в алгоритме Ахо-Корасик, но в модифицированном виде

К сожалению, иногда trie может "съесть" много памяти, если получилось так, что под каждый символ каждой хранимой последовательности память выделяется отдельно

Структура данных

Trie - рекурсивная структура данных. И ее определение рекурсивно:

struct Trie {
    value,  -- значение любого типа для хранения в узле
    children: Mapping of type (char : pointer to Trie)  -- ассоциативный массив с указателями на другие trie
}

Поскольку trie - ассоциативный массив, он должен поддердживать:

  • вставку, Insert(key, value)
  • удаление, Delete(key)
  • поиск, Search(key)
  • Опционально, поскольку Trie во многом похоже на деревья, операцию обхода Traverse

Абстрактные вопросы

Можно ли сделать так, чтобы множество trie'в стало

  • полугруппой?
  • моноидом? (ответ, еще один - см. instance Monoid)
  • коммутативной полугруппой?

Сжатое префиксное дерево

Рассмотрим следующую ситуацию: trie испольуют, например, для задачи сохранения всех возможных суффиксов строки.

Строка: "AAABAAAB"

Вот что мы получим после сжатия Внимание! Сжаты не все возможные узлы, у только набор самых "вопиющих", находящийся слева.

Еще один пример

Внимание, ошибка! На самом деле нет узла "упила", а еслть узел "пила", выходящий из узла "у".

</font>

Алгоритм Ахо-Корасик

Автомат или trie?

  • Синие стрелки обозначают суффиксные ссылки (т.е. ссылки на суффикс, который встречается в дереве в одном из узлов)
  • Это небольшая модификация переходов к префиксам, которые при этом являются и суффиксами, в конечных автоматах для разбора строк.

А если взять два автомата?

Автоматы можно сложить!
Серые линии - "пропавшие" суффиксы. Они не нужны, так как мы выбираем наибольший из присутствующих в атомате, то есть в trie, суффиксов.

Автомат может обрабатывать следующие строки: AGAG, GTAG

Промежуточный итог:
Чтобы расчитать суффиксные ссылки, для каждого узла в trie составляется набор суффиксов. Если суффикс присутсвует в trie, до от узла до этого суффикса проставляется ссылка.

Словарные суффиксы

Зеленые стрелки указывают на словарные суффиксы. То есть на те суффиксы, которые сами являются целевыми словами.

Обратите внимание на зеленую стрелку, крайнюю слева внизу (выделена пунктиром). Непосредственно два узла A и A суффиксными ссылками не соединены, но соединены через узел A в правом ряду суффиксными ссылками (так же выделены).

Правила постоения "словарно-суффиксных" ссылок просты: если по суффиксным ссылкам можно дойти от текущего узла до узла, который так же является и словарным суффиксом, то между началом и концом пути проставляется словарная ссылка.

Работа автомата

Итак, автомат на изображении выше ищет следующие строки: A, G, CG, CG, CGA, GAA. Если в тексте встречается любая из них, автомат должен сообщить об этом. Если строки встречаются "одновременно", например, после символа C идет G - автомат должен сообщить о двух найденных строках, CG и G.

Разберем поиск в строке CACGAA


Узел     Остаток     Результат
----------------------------
()    -   CACGAA  -  None
(C)   -    ACGAA  -  None
(A)   -     CGAA  -  A
(C)   -      GAA  -  None 
(CG)  -       AA  -  CG, G
(CGA) -        A  -  CGA, A
(GAA) -        ε  -  GAA

Правила обхода

Обход trie в алгоритме Ахо-Корасик:

  • На каждом шаге из текущего ущла переходить в следующий, если у него есть ребенок. Если ребенка нет, то переходить в суффикс и искать его детей. Если у суффикса у детей нет, то переходить в суффикс суффикса и т.д... рекурсивно до корня.

</font>

Домашняя работа

Основное задание (Сделано любое задание из двух - 2 балла, два из двух - 3 балла):

  • Реализовать trie для хранения строк (тем, кто делал Radix Sort через trie, повезло!).
  • Написать алгоритм, который для одной строки строит trie и рассчитывает суффиксные ссылки Дополнительные задания (1 балл за каждое):
  • Написать код для "сложения" (слияния) двух trie
  • Написать алгоритм, строящий дерево с суффиксными ссылками для произвольно количества строк
  • Сделать сжатое префиксное дерево

</font>

In [ ]:
 
In [ ]: