Appearance
Бор
Нужна структура для множества (или словаря) строк, где помимо ответа на вопрос «есть ли строка S?» можно также ответить на вопросы про префиксы: сколько слов начинается с какого-то префикса, перечислить их, дополнить структуру новым словом.
Примем следующие обозначения:
— алфавит, — его размер — число строк в наборе; — суммарная длина всех строк; — длина запроса.
1. Определение и структура
Бор (префиксное дерево, нагруженное дерево, trie) — подвешенное дерево, у которого:
- на каждом ребре записан символ алфавита;
- у одной вершины нет двух исходящих ребер с одинаковым символом;
- часть вершин помечены терминальными - это вершины, в которых заканчиваются какие-то слова из набора строк
Бор хранит ровно те строки, которые получаются, если выписать подряд символы на пути от корня до терминальной вершины.
(В некоторых источниках символ хранят не на ребре, а в вершине-ребенке. Это эквивалентно: символ ребра «корень → v» — это символ вершины v. Мы пишем символы на ребрах.)
Пример. Набор

Из определения строения бора следует:
- Общие префиксы хранятся один раз — отсюда экономия по сравнению со списком строк.
- Терминальность нужна отдельно: в примере
he— префиксhers, но и сам элемент набора; вершинаheне лист, но терминальна. Листья всегда терминальны. - Вершин не больше n + 1, глубина равна длине самой длинной строки.
- Строку однозначно определяет путь, поэтому ее не нужно хранить в вершине.
В каждой вершине будут хранится ссылки на детей (переходы по какому-то символу), bool терминальности + еще какие-то переменные в зависимости от задачи
2. Операции
2.1 Вставка
Идем от корня по символам строки. Если перехода нет — создаем вершину. В конце отмечаем последнюю вершину терминальной.
Допустим есть два слова: gene и genetic. В зависимости от того в каком порядке вставляем слова получится два случая:
- вставляем genetic, когда есть gene: идем по gene, создаем вершины
, , , отмечаем ; - вставляем gene, когда есть genetic: новых вершин нет, только отметка терминальности.
Время:
2.2 Поиск
Идем по символам. Возможны три исхода:
- Не нашли переход — строки в боре нет.
- Дошли до конца строки, но вершина не терминальна — строки нет (это лишь префикс другой строки).
- Дошли до конца строки в терминальной вершине — строка есть.
Время:
3. Бинарный бор и задачи на XOR
3.1 Идея
Алфавит состоит из двух символов — {0, 1}. Целое неотрицательное число
В вершине храним двух детей и счетчик cnt — сколько чисел проходит через вершину.
Вставка, поиск и удаление:
Полезные свойства:
- DFS с порядком «сначала 0, потом 1» выдает числа*по возрастанию
- минимум — идти всегда в 0 (если можно), максимум — в 1;
- k-й по величине элемент и «сколько элементов меньше x» — спуск по
cnt, как в дереве порядковых статистик.
3.2 Задача 1. Максимальный XOR числа x с элементом множества
Дано: непустое множество чисел и число x. Найти: max по y из множества значение
Идем от старшего бита к младшему. На каждом шаге хотим, чтобы бит результата был 1, то есть выбрать ребенка с битом, противоположным биту
Единица в бите
3.3 Задача 2. Максимальный XOR пары в массиве
Кладем в бор все числа массива и для каждого делаем max_xor из прошлой задачи, либо идем слева направо и запрашиваем max_xor до вставки очередного числа.
Сложность будет
3.4 Задача 3. Максимальный XOR подотрезка
XOR отрезка a[l..r] равен p[r] ⊕ p[l−1], где p[i] — XOR префикса длины i (p[0] = 0). Значит, задача сводится к предыдущей на массиве префиксных XOR.
Время и память