Сжатое суффиксное дерево — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
Строка 1: Строка 1:
[[Суффиксный бор|Суффиксный бор]] {{---}} удобная структура для поиска подстроки в строке, но занимающая много места в памяти. Рассмотрим все такие пути от <tex>u</tex> до  <tex>v</tex> в суффиксном боре, в которых каждая вершина имеет только одного сына. Такой путь можно сжать до одного ребра <tex>u v</tex>, пометив его всеми встречающимися на пути символами. Получившееся дерево носит название '''сжатое суффиксное дерево'''.
+
[[Суффиксный бор|Суффиксный бор]] {{---}} удобная структура данных для поиска подстроки в строке, но она занимает много места в памяти. Рассмотрим в боре все пути от <tex>u</tex> до  <tex>v</tex>, в которых у каждой вершины только один сын. Такой путь можно сжать до ребра <tex>u v</tex>, записав на нем все встречающиеся на пути символы. Получилось '''сжатое суффиксное дерево'''.
  
 
==Определение==
 
==Определение==
'''Суффиксное дерево''' (сжатое суффиксное дерево) <tex>T</tex> для строки <tex>s</tex> (где  <tex>|s| = n</tex>) {{---}} ориентированное дерево с <tex>n</tex> листьями, каждая внутренняя вершина которого имеет не меньше двух детей, а каждое ребро помечено непустой подстрокой строки <tex>s</tex> и символом, с которого начинается эта подстрока. При этом два ребра, выходящие из одной вершины, не могут иметь одинаковых символьных пометок. Такое дерево содержит все суффиксы строки <tex>s</tex>: для каждого листа <tex>i</tex> конкатенация подстрок на ребрах пути от корня к этому листу составляет суффикс, который начинается в позиции <tex>i</tex>, то есть <tex>s[i..n]</tex>. Иными словами, каждый суффикс строки <tex>s</tex> заканчивается точно в листе и нигде кроме листа, как и в суффиксном боре.
+
'''Суффиксное дерево''' (сжатое суффиксное дерево) <tex>T</tex> для строки <tex>s</tex> (где  <tex>|s| = n</tex>) {{---}} дерево с <tex>n</tex> листьями, каждая внутренняя вершина которого имеет не меньше двух детей, а каждое ребро помечено непустой подстрокой строки <tex>s</tex> и символом ее начала. Два ребра, выходящие из одной вершины, не могут иметь одинаковых символьных меток.  
 +
Такое дерево, как и суффиксный бор, содержит все суффиксы строки <tex>s</tex>, причем каждый суффикс заканчивается точно в листе и нигде кроме него.
  
 
==Защитный символ==
 
==Защитный символ==
 
[[Файл:Suffix_tree_3.png|thumb|right|Суффиксное дерево для строки <tex>xabxa</tex> с защитным символом]]
 
[[Файл:Suffix_tree_3.png|thumb|right|Суффиксное дерево для строки <tex>xabxa</tex> с защитным символом]]
Определение суффиксного дерева не гарантирует, что такое дерево существует для любой строки <tex>s</tex>. Если один суффикс строки совпадает с префиксом другого, то построить суффиксное дерево, удовлетворяющее данному выше определению, невозможно. Например, для строки <tex>xabxa</tex> суффикс <tex>xa</tex> является префиксом суффикса <tex>xabxa.</tex> Во избежание этого в конце строки <tex>s</tex> добавляется символ, не входящий в исходный алфавит. Такой символ называется '''''защитным'''''. Как правило, это <tex>\$</tex>. Любой суффикс строки с защитным символом заканчивается в листе и только в листе, т.к. он не встречается в строке нигде, кроме позиции последнего символа.
+
По определению суффиксное дерево существует не для любой строки <tex>s</tex>: если один суффикс строки совпадает с префиксом другого, то построить такое суффиксное дерево невозможно. Например, для строки <tex>xabxa</tex> суффикс <tex>xa</tex> является префиксом суффикса <tex>xabxa.</tex> Для решения проблемы в конце строки <tex>s</tex> добавляется символ, не входящий в исходный алфавит: '''''защитный''''' символ. Как правило, это <tex>\$</tex>. Любой суффикс строки с защитным символом действительно заканчивается в листе и только в листе.
  
 
Далее <tex>n</tex> - длина строки <tex>s</tex> с защитным символом.
 
Далее <tex>n</tex> - длина строки <tex>s</tex> с защитным символом.
  
 
==Хранение суффиксного дерева==
 
==Хранение суффиксного дерева==
Каждое ребро суффиксного дерева помечается подстрокой исходной строки <tex>s</tex>. Но лучше для него хранить не саму подстроку, а индексы ее начала и конца в исходной строке {{---}} <tex>l, r</tex>. Итак, с каждым ребром дерева ассоциируются две инцидентные ей вершины, символ, с которого начинается подстрока на ребре и два числа <tex>l, r</tex>. Представим дерево как массив <tex>[|V|*|\Sigma|]</tex>, где <tex>|V|</tex> {{---}} количество вершин в дереве, <tex>|\Sigma|</tex> - мощность алфавита. Каждая <tex>[i][j]</tex> ячейка массива содержит информацию о том, в какую вершину ведет <tex>i-</tex>ое ребро по <tex>j-</tex>ому символу и индексы <tex>l, r</tex> подстроки на ребре. Очевидно, такое дерево занимает <tex>O(|V||\Sigma|)</tex> памяти.
+
Для хранения на ребре подстроки используют индексы ее начала и конца в исходной строке {{---}} <tex>l, r</tex>. Итак, с каждым ребром дерева ассоциируются две инцидентные ей вершины, символ, с которого начинается подстрока на ребре и два числа <tex>l, r</tex>. Представим дерево как массив <tex>[|V|*|\Sigma|]</tex>, где <tex>|V|</tex> {{---}} количество вершин в дереве, <tex>|\Sigma|</tex> - мощность алфавита. Каждая <tex>[i][j]</tex> ячейка массива содержит информацию о том, в какую вершину ведет <tex>i-</tex>ое ребро по <tex>j-</tex>ому символу и индексы <tex>l, r</tex> подстроки на ребре. Очевидно, такое дерево занимает <tex>O(|V||\Sigma|)</tex> памяти.
  
 
==Количество вершин==
 
==Количество вершин==

Версия 21:59, 31 мая 2012

Суффиксный бор — удобная структура данных для поиска подстроки в строке, но она занимает много места в памяти. Рассмотрим в боре все пути от [math]u[/math] до [math]v[/math], в которых у каждой вершины только один сын. Такой путь можно сжать до ребра [math]u v[/math], записав на нем все встречающиеся на пути символы. Получилось сжатое суффиксное дерево.

Определение

Суффиксное дерево (сжатое суффиксное дерево) [math]T[/math] для строки [math]s[/math] (где [math]|s| = n[/math]) — дерево с [math]n[/math] листьями, каждая внутренняя вершина которого имеет не меньше двух детей, а каждое ребро помечено непустой подстрокой строки [math]s[/math] и символом ее начала. Два ребра, выходящие из одной вершины, не могут иметь одинаковых символьных меток. Такое дерево, как и суффиксный бор, содержит все суффиксы строки [math]s[/math], причем каждый суффикс заканчивается точно в листе и нигде кроме него.

Защитный символ

Суффиксное дерево для строки [math]xabxa[/math] с защитным символом

По определению суффиксное дерево существует не для любой строки [math]s[/math]: если один суффикс строки совпадает с префиксом другого, то построить такое суффиксное дерево невозможно. Например, для строки [math]xabxa[/math] суффикс [math]xa[/math] является префиксом суффикса [math]xabxa.[/math] Для решения проблемы в конце строки [math]s[/math] добавляется символ, не входящий в исходный алфавит: защитный символ. Как правило, это [math]\$[/math]. Любой суффикс строки с защитным символом действительно заканчивается в листе и только в листе.

Далее [math]n[/math] - длина строки [math]s[/math] с защитным символом.

Хранение суффиксного дерева

Для хранения на ребре подстроки используют индексы ее начала и конца в исходной строке — [math]l, r[/math]. Итак, с каждым ребром дерева ассоциируются две инцидентные ей вершины, символ, с которого начинается подстрока на ребре и два числа [math]l, r[/math]. Представим дерево как массив [math][|V|*|\Sigma|][/math], где [math]|V|[/math] — количество вершин в дереве, [math]|\Sigma|[/math] - мощность алфавита. Каждая [math][i][j][/math] ячейка массива содержит информацию о том, в какую вершину ведет [math]i-[/math]ое ребро по [math]j-[/math]ому символу и индексы [math]l, r[/math] подстроки на ребре. Очевидно, такое дерево занимает [math]O(|V||\Sigma|)[/math] памяти.

Количество вершин

В сжатом суффиксном дереве содержится [math]n[/math] листьев, т.к. строка [math]s[/math] содержит ровно [math]n[/math] суффиксов. Рассмотрим теперь количество внутренних вершин такого дерева.

Лемма:
Количество внутренних вершин дерева, каждая из которых имеет не менее двух детей, меньше количества листьев.
Доказательство:
[math]\triangleright[/math]

Докажем лемму индукцией по количеству листьев [math]n[/math].

База

При [math]n = 2[/math] в дереве одна внутренняя вершина - верно.

Переход [math]n \rightarrow n + 1[/math]

Рассмотрим все вершины в дереве для строки длины [math]n + 1[/math], у которых хотя бы один из детей - лист.

Если среди них есть вершина, у которой более двух детей, отрежем от нее лист. Получим дерево с [math]n[/math] листьями, удовлетворяющее условию леммы по индукционному предположению, причем в нем количество внутренних вершин равно количеству внутренних вершин в исходном дереве. Тогда у полученного дерева менее [math]n[/math] внутренних вершин, значит в исходном дереве количество внутренних вершин меньше количества листьев.

Иначе среди этих вершин есть вершина, у которой оба ребенка - листья. Отрежем оба этих листа, получим дерево с [math]n[/math] листьями, удовлетворяющее условию леммы, количество внутренних вершин которого на [math]1[/math] меньше количества внутренних вершин в исходном дереве. Тогда, по индукционному предположению, у полученного дерева менее [math]n[/math] внутренних вершин, значит в исходном дереве количество внутренних вершин меньше [math]n + 1[/math].
[math]\triangleleft[/math]

Занимаемая память

Так как любое суффиксное дерево удовлетворяет условиям леммы (у каждой вершины не менее двух детей), то количество внутренних вершин в нем меньше количества листьев, равного [math]n[/math]. Значит, для его хранения требуется [math]O(n|\Sigma|)[/math] памяти.

Построение суффиксного дерева

Рассмотрим наивный алгоритм построения суффиксного дерева:

for [math] i \leftarrow 0 [/math] to [math] n [/math] do //для каждого символа строки
    insert([math]i, n[/math]) //добавляем суффикс, начинающийся с него
insert(l,r)
    [math] cur \leftarrow root [/math] //инициализируем текущую вершину корнем
    while ([math] i \lt  r [/math])
         if [math] go[cur][s[i]].v = 0 [/math] //если мы не можем пойти из вершины по символу [math] i [/math]
              create_vertex([math]cur, new V, i, r, s[i][/math]) //создаем новую вершину
         else
              [math]start \leftarrow go[cur][s[i]].l [/math]
              [math]finish \leftarrow go[cur][s[i]].r [/math]
              for [math] j = start [/math] to [math] finish [/math] //для каждого символа на ребре из текущей вершины
                   if [math]s[i+j-start] \lt \gt s[j] [/math] //если нашли не совпадающий символ
                        разбить ребро
                        break
              if ребро не разбивали
                   [math]cur \leftarrow go[cur][s[i]].v [/math] //переходим по ребру
                   [math]i \leftarrow i + finish - start [/math] //двигаемся по суффиксу на длину подстроки, записанной на ребре

Этот алгоритм работает за время [math]O(n^2)[/math], однако существует алгоритм Укконена, позволяющий построить дерево за время [math]O(n)[/math].

Использование сжатого суффиксного дерева

Суффиксное дерево позволяет за линейное время найти:

  • Количество различных подстрок данной строки
  • Наибольшую общую подстроку двух строк
  • Суффиксный массив и массив [math]lcp[/math] (longest common prefix) исходной строки

Источники

  • Дэн ГасфилдСтроки, деревья и последовательности в алгоритмах: Информатика и вычислительная биология — СПб.: Невский Диалект; БХВ-Петербург, 2003. — 654 с: ил.