Сжатое суффиксное дерево — различия между версиями
(→Хранение суффиксного дерева) |
|||
| Строка 10: | Строка 10: | ||
Далее <tex>n</tex> - длина строки <tex>s</tex> с защитным символом. | Далее <tex>n</tex> - длина строки <tex>s</tex> с защитным символом. | ||
| − | |||
| − | |||
| − | |||
==Количество вершин== | ==Количество вершин== | ||
| − | + | По определению, в суффиксном дереве содержится <tex>n</tex> листьев. Рассмотрим количество внутренних вершин такого дерева. | |
{{Лемма | {{Лемма | ||
| Строка 29: | Строка 26: | ||
'''Переход''' <tex>n \rightarrow n + 1</tex> | '''Переход''' <tex>n \rightarrow n + 1</tex> | ||
| − | Рассмотрим | + | Рассмотрим вершину в дереве с <tex>n + 1</tex> листами, у которой два ребенка - листья. |
| − | + | 1) У нее более двух детей. Тогда отрежем от нее лист. Получим дерево с <tex>n</tex> листьями, причем в нем количество внутренних вершин такое же, как в исходном дереве. Но у полученного дерева менее <tex>n</tex> внутренних вершин (т.к. по индукционному предположению для него выполняется условие леммы), значит, для исходного дерева лемма верна. | |
| − | + | 2) У нее ровно два ребенка. Отрежем их, получим дерево с <tex>n</tex> листьями, количество внутренних вершин которого на <tex>1</tex> меньше, чем в исходном дереве. Тогда, по индукционному предположению, у него менее <tex>n</tex> внутренних вершин, значит в исходном дереве их меньше <tex>n + 1</tex>. | |
}} | }} | ||
==Занимаемая память== | ==Занимаемая память== | ||
| − | + | Заметим, что для хранения на ребре подстроки используют индексы <tex>l, r</tex> ее начала и конца в исходной строке, а не хранят саму строку. Представим теперь дерево как массив <tex>[|V|*|\Sigma|]</tex>, где <tex>|V|</tex> {{---}} количество вершин в дереве, <tex>|\Sigma|</tex> - мощность алфавита. Для любого суффиксного дерева верна предыдущая лемма (у каждой вершины по определению не менее двух детей), значит, <tex>|V| = O(2*n)</tex>. Каждая <tex>[i][j]</tex> ячейка содержит информацию о том, в какую вершину ведет <tex>i-</tex>ое ребро по <tex>j-</tex>ому символу и индексы <tex>l, r</tex>. Итак, дерево занимает <tex>O(n*|\Sigma|)</tex> памяти. | |
==Построение суффиксного дерева== | ==Построение суффиксного дерева== | ||
| Строка 45: | Строка 42: | ||
insert(l,r) | insert(l,r) | ||
| − | <tex> cur \leftarrow root </tex> | + | <tex> cur \leftarrow root </tex> |
'''while''' (<tex> i < r </tex>) | '''while''' (<tex> i < r </tex>) | ||
'''if''' <tex> go[cur][s[i]].v = 0 </tex> //если мы не можем пойти из вершины по символу <tex> i </tex> | '''if''' <tex> go[cur][s[i]].v = 0 </tex> //если мы не можем пойти из вершины по символу <tex> i </tex> | ||
| Строка 60: | Строка 57: | ||
<tex>i \leftarrow i + finish - start </tex> //двигаемся по суффиксу на длину подстроки, записанной на ребре | <tex>i \leftarrow i + finish - start </tex> //двигаемся по суффиксу на длину подстроки, записанной на ребре | ||
| − | Этот алгоритм работает за время <tex>O(n^2)</tex>, однако | + | Этот алгоритм работает за время <tex>O(n^2)</tex>, однако [[Алгоритм Укконена| алгоритм Укконена]], позволяет построить сжатое суффиксное дерево за <tex>O(n)</tex>. |
==Использование сжатого суффиксного дерева== | ==Использование сжатого суффиксного дерева== | ||
Версия 23:08, 31 мая 2012
Суффиксный бор — удобная структура данных для поиска подстроки в строке, но она занимает много места в памяти. Рассмотрим в боре все пути от до , в которых у каждой вершины только один сын. Такой путь можно сжать до ребра , записав на нем все встречающиеся на пути символы. Получилось сжатое суффиксное дерево.
Содержание
Определение
Суффиксное дерево (сжатое суффиксное дерево) для строки (где ) — дерево с листьями, каждая внутренняя вершина которого имеет не меньше двух детей, а каждое ребро помечено непустой подстрокой строки и символом ее начала. Два ребра, выходящие из одной вершины, не могут иметь одинаковых символьных меток. Такое дерево, как и суффиксный бор, содержит все суффиксы строки , причем каждый суффикс заканчивается точно в листе и нигде кроме него.
Защитный символ
По определению суффиксное дерево существует не для любой строки : если один суффикс строки совпадает с префиксом другого, то построить такое суффиксное дерево невозможно. Например, для строки суффикс является префиксом суффикса Для решения проблемы в конце строки добавляется символ, не входящий в исходный алфавит: защитный символ. Как правило, это . Любой суффикс строки с защитным символом действительно заканчивается в листе и только в листе.
Далее - длина строки с защитным символом.
Количество вершин
По определению, в суффиксном дереве содержится листьев. Рассмотрим количество внутренних вершин такого дерева.
| Лемма: |
Количество внутренних вершин дерева, каждая из которых имеет не менее двух детей, меньше количества листьев. |
| Доказательство: |
|
Докажем лемму индукцией по количеству листьев . База При в дереве одна внутренняя вершина - верно. Переход Рассмотрим вершину в дереве с листами, у которой два ребенка - листья. 1) У нее более двух детей. Тогда отрежем от нее лист. Получим дерево с листьями, причем в нем количество внутренних вершин такое же, как в исходном дереве. Но у полученного дерева менее внутренних вершин (т.к. по индукционному предположению для него выполняется условие леммы), значит, для исходного дерева лемма верна. 2) У нее ровно два ребенка. Отрежем их, получим дерево с листьями, количество внутренних вершин которого на меньше, чем в исходном дереве. Тогда, по индукционному предположению, у него менее внутренних вершин, значит в исходном дереве их меньше . |
Занимаемая память
Заметим, что для хранения на ребре подстроки используют индексы ее начала и конца в исходной строке, а не хранят саму строку. Представим теперь дерево как массив , где — количество вершин в дереве, - мощность алфавита. Для любого суффиксного дерева верна предыдущая лемма (у каждой вершины по определению не менее двух детей), значит, . Каждая ячейка содержит информацию о том, в какую вершину ведет ое ребро по ому символу и индексы . Итак, дерево занимает памяти.
Построение суффиксного дерева
Рассмотрим наивный алгоритм построения суффиксного дерева:
for to do //для каждого символа строки insert() //добавляем суффикс, начинающийся с него
insert(l,r)
while ()
if //если мы не можем пойти из вершины по символу
create_vertex() //создаем новую вершину
else
for to //для каждого символа на ребре из текущей вершины
if //если нашли не совпадающий символ
разбить ребро
break
if ребро не разбивали
//переходим по ребру
//двигаемся по суффиксу на длину подстроки, записанной на ребре
Этот алгоритм работает за время , однако алгоритм Укконена, позволяет построить сжатое суффиксное дерево за .
Использование сжатого суффиксного дерева
Суффиксное дерево позволяет за линейное время найти:
- Количество различных подстрок данной строки
- Наибольшую общую подстроку двух строк
- Суффиксный массив и массив (longest common prefix) исходной строки
Источники
- Дэн Гасфилд — Строки, деревья и последовательности в алгоритмах: Информатика и вычислительная биология — СПб.: Невский Диалект; БХВ-Петербург, 2003. — 654 с: ил.