Сжатое суффиксное дерево — различия между версиями
м |
(→Построение суффиксного дерева) |
||
Строка 43: | Строка 43: | ||
==Построение суффиксного дерева== | ==Построение суффиксного дерева== | ||
Рассмотрим наивный алгоритм построения суффиксного дерева: | Рассмотрим наивный алгоритм построения суффиксного дерева: | ||
+ | <tex>count \leftarrow 0</tex> //номер последней вершины, созданной в дереве (глобальная переменная) | ||
'''for''' <tex> i \leftarrow 0 </tex> '''to''' <tex> n </tex> '''do''' //для каждого символа строки | '''for''' <tex> i \leftarrow 0 </tex> '''to''' <tex> n </tex> '''do''' //для каждого символа строки | ||
insert(<tex>i, n</tex>) //добавляем суффикс, начинающийся с него | insert(<tex>i, n</tex>) //добавляем суффикс, начинающийся с него | ||
− | insert(l,r) | + | insert(l, r) |
<tex> cur \leftarrow root </tex> | <tex> cur \leftarrow root </tex> | ||
− | '''while''' (<tex> | + | '''while''' (<tex> l < r </tex>) |
− | '''if''' <tex> go[cur][s[i]].v = 0 </tex> //если мы не можем пойти из вершины по символу <tex> | + | '''if''' <tex> go[cur][s[i]].v = 0 </tex> //если мы не можем пойти из вершины по символу <tex> l </tex> |
− | + | createVertex(<tex>cur, l, r</tex>) //создаем новую вершину | |
'''else''' | '''else''' | ||
<tex>start \leftarrow go[cur][s[i]].l </tex> | <tex>start \leftarrow go[cur][s[i]].l </tex> | ||
<tex>finish \leftarrow go[cur][s[i]].r </tex> | <tex>finish \leftarrow go[cur][s[i]].r </tex> | ||
+ | <tex>hasBreak \leftarrow false </tex> | ||
'''for''' <tex> j = start </tex> '''to''' <tex> finish </tex> //для каждого символа на ребре из текущей вершины | '''for''' <tex> j = start </tex> '''to''' <tex> finish </tex> //для каждого символа на ребре из текущей вершины | ||
'''if''' <tex>s[i+j-start] <>s[j] </tex> //если нашли не совпадающий символ | '''if''' <tex>s[i+j-start] <>s[j] </tex> //если нашли не совпадающий символ | ||
− | + | newEdge(<tex>cur, l, j, r</tex>) //создаем вершину на ребре | |
+ | <tex>hasBreak \leftarrow true </tex> | ||
'''break''' | '''break''' | ||
− | '''if''' | + | '''if''' <tex>!hasBreak</tex> |
<tex>cur \leftarrow go[cur][s[i]].v </tex> //переходим по ребру | <tex>cur \leftarrow go[cur][s[i]].v </tex> //переходим по ребру | ||
− | <tex> | + | <tex>l \leftarrow l + finish - start </tex> //двигаемся по суффиксу на длину подстроки, записанной на ребре |
+ | |||
+ | createVertex(<tex>cur, l, r</tex>) | ||
+ | <tex>go[cur][s[l]] \leftarrow new Vertex()</tex> | ||
+ | <tex>go[cur][s[l]].v \leftarrow count</tex> | ||
+ | <tex>go[cur][s[l]].l \leftarrow l</tex> | ||
+ | <tex>go[cur][s[l]].r \leftarrow r</tex> | ||
+ | <tex>count++</tex> | ||
+ | |||
+ | newEdge(<tex>cur, i, j, r</tex>) | ||
+ | <tex>next \leftarrow go[cur][s[l]].v</tex> | ||
+ | createVertex(<tex>cur, l, j - 1</tex>) | ||
+ | <tex>cur \leftarrow go[cur][s[l]].v</tex> | ||
+ | createVertex(<tex>cur, j, r</tex>) | ||
+ | <tex>go[cur][s[j]].v \leftarrow next </tex> | ||
+ | <tex>go[cur][s[j]].l \leftarrow j </tex> | ||
+ | <tex>go[cur][s[j]].r \leftarrow r </tex> | ||
+ | |||
+ | |||
+ | |||
Этот алгоритм работает за время <tex>O(n^2)</tex>, однако [[Алгоритм Укконена| алгоритм Укконена]] позволяет построить сжатое суффиксное дерево за <tex>O(n)</tex>. | Этот алгоритм работает за время <tex>O(n^2)</tex>, однако [[Алгоритм Укконена| алгоритм Укконена]] позволяет построить сжатое суффиксное дерево за <tex>O(n)</tex>. |
Версия 23:53, 1 июня 2012
Суффиксный бор — удобная структура данных для поиска подстроки в строке, но она занимает много места в памяти. Рассмотрим в боре все пути от до , в которых у каждой вершины только один сын. Такой путь можно сжать до ребра , записав на нем все встречающиеся на пути символы, которые являются подстрокой исходной строки. Для хранения ее на ребре обычно используют индексы начала и конца. Получилось сжатое суффиксное дерево.
Содержание
Определение
Определение: |
Суффиксное дерево (сжатое суффиксное дерево)
| для строки (где ) — дерево с листьями, обладающее следующими свойствами:
Рассмотрим дерево для строки
. У него суффикс является префиксом суффикса , значит, этот суффикс не закачивается в листе. Для решения проблемы в конце строки добавляют символ, не входящий в исходный алфавит: защитный символ. Как правило, это . Любой суффикс строки с защитным символом действительно заканчивается в листе и только в листе.Далее
- длина строки с защитным символом.Количество вершин
По определению, в суффиксном дереве содержится
листьев. Рассмотрим количество внутренних вершин такого дерева.Лемма: |
Количество внутренних вершин дерева, каждая из которых имеет не менее двух детей, меньше количества листьев. |
Доказательство: |
Докажем лемму индукцией по количеству листьев .База При в дереве одна внутренняя вершина - верно.Переход Возьмем вершину в дереве с листами, у которой два ребенка - листья. Рассмотрим возможные случаи:1) У нее более двух детей. Тогда отрежем от нее лист. Получим дерево с 2) У нее ровно два ребенка. Отрежем их, получим дерево с листьями, причем в нем количество внутренних вершин такое же, как в исходном дереве. Но у полученного дерева по индукционному предположению менее внутренних вершин, значит, для исходного дерева лемма верна. листьями, количество внутренних вершин которого на меньше, чем в исходном дереве. Тогда по индукционному предположению у него менее внутренних вершин, значит, в исходном дереве их меньше . |
Занимаемая память
Представим дерево как массив
, где — количество вершин в дереве, - мощность алфавита. Для любого суффиксного дерева верна предыдущая лемма (у каждой вершины по определению не менее двух детей), значит, . Каждая ячейка содержит информацию о том, в какую вершину ведет ое ребро по ому символу и индексы . Итак, дерево занимает памяти.Построение суффиксного дерева
Рассмотрим наивный алгоритм построения суффиксного дерева:
//номер последней вершины, созданной в дереве (глобальная переменная) for to do //для каждого символа строки insert( ) //добавляем суффикс, начинающийся с него
insert(l, r)while ( ) if //если мы не можем пойти из вершины по символу createVertex( ) //создаем новую вершину else for to //для каждого символа на ребре из текущей вершины if //если нашли не совпадающий символ newEdge( ) //создаем вершину на ребре break if //переходим по ребру //двигаемся по суффиксу на длину подстроки, записанной на ребре
createVertex()
newEdge() createVertex( ) createVertex( )
Этот алгоритм работает за время , однако алгоритм Укконена позволяет построить сжатое суффиксное дерево за .
Использование сжатого суффиксного дерева
Суффиксное дерево позволяет за линейное время найти:
- Количество различных подстрок данной строки
- Наибольшую общую подстроку двух строк
- Суффиксный массив и массив (longest common prefix) исходной строки
Источники
- Дэн Гасфилд — Строки, деревья и последовательности в алгоритмах: Информатика и вычислительная биология — СПб.: Невский Диалект; БХВ-Петербург, 2003. — 654 с: ил.