Изменения

Перейти к: навигация, поиск

Алгоритм Фараха

416 байт убрано, 18:49, 16 июня 2015
Описание алгоритма
=== Шаг 1: суффиксное дерево для сжатой строки===
[[Файл:tree101232.png|thumb|300px|суффиксное дерево для сжатой строки]]
* Строка <tex>s</tex> разбивается на пары подряд идущих символов: <tex> \langle 12\rangle \langle 11\rangle \langle 12\rangle \langle 21\rangle \langle 22\rangle \langle 21\rangle </tex>
*: (если символов нечетное число {{---}} последняя пара дополняется специальным символом <tex>\$</tex>)
* В исходной строке пары заменяются на номера: <tex>1 0 1 2 3 2</tex>
* Из полученной строки вдвое меньшего размера рекурсивно создаётся [[Сжатое суффиксное дерево | суффикcное дерево]] тем же алгоритмом:
[[Файл:tree101232.png|thumb|300px|суффиксное дерево для сжатой строки]]
* рекурсия не продолжается, если строка имеет длину <tex>1</tex>: суффиксное дерево строится тривиально.
|definition= Четное дерево <tex>T^{even}_s</tex> является деревом суффиксов для строки <tex>s</tex>, узлы-листья
которого ограничены четными позициями <tex>0,2,4,6, \dots </tex> строки <tex>s\$</tex>.}}
 
[[Файл:Tree101232even-pre.png|thumb|300px|Раскрываем все пары в суффиксы]]
[[Файл:Tree101232even.png|thumb|300px|Корректируются все развилки дерева (так как они могут совпадать в первых символах)]]
Из дерева сжатой строки получаем частичное (чётное) дерево исходной строки. Частичное оно потому, что в нём будет только половина суффиксов, то есть те, которые стоят в чётных позициях.
Номер каждой пары превращается в номер четного суффикса исходной строки. Раскрываем все пары в суффиксы, а номера в листьях от этого умножатся на <tex>2</tex> очевидным образом:
 
[[Файл:Tree101232even-pre.png|thumb|300px|Раскрываем все пары в суффиксы]]
 
'''Рис. 2 дерево с раскрытыми парами в суффиксы'''
Корректируются все развилки дерева (так как они могут совпадать в первых символах):
для всех внутренних вершин <tex>u</tex>, ребра всех детей которых начинаются с одинаковых символов, мы создадим новую вершину между <tex>u</tex> и ее детьми. Это можно сделать быстро, так как все ребра, исходящие из любой вершины, лексикографически отсортированы по своим первым двум символам (так как мы сортировали номера пар на прошлом шаге). Для каждого ребра нам достаточно проверить, что его первый символ соответствует первому символу соседнего ребра, и, если так, сделать необходимые исправления. Может быть, что ребра ко всем детям <tex>u</tex> начинаются с одинакового символа, и в этом случае у вершины <tex>u</tex> будет только один ребенок. Тогда удалим <tex>u</tex>.
Эта процедура требует константное время на каждое ребро и константное время на каждую вершину, а значит, на нее требуется линейное время.
 
[[Файл:Tree101232even.png|thumb|300px|Корректируются все развилки дерева (так как они могут совпадать в первых символах)]]
 
'''Рис. 3 все развилки скорректированы'''
Итак, если <tex>T(n)</tex> {{---}} это время, которое потребуется нашему алгоритму, чтобы построить суффиксное дерево для строки <tex>S</tex>, то <tex>T_{even}</tex> может быть построено за время <tex>T(n/2) + O(n)</tex>
|definition= Нечётное дерево <tex>T^{odd}_s</tex> является деревом суффиксов для строки <tex>s</tex>, узлы-листья
которого ограничены нечетными позициями <tex>1,3,5, \dots </tex> строки <tex>s\$</tex>.}}
 
[[Файл:Odd.png|thumb|450px|нечетное дерево]]
Из чётного дерева нужно получить нечётное дерево (дерево из суффиксов в нечётных позициях).
* Заметим, что все нечетные суффиксы представляют собой один символ, за которым дальше следует четный суффикс. А чётные суффиксы у нас уже были отсортированы в суффиксном массиве. Тогда отсортируем их по первому символу за линейное время.
* [[Сжатое_суффиксное_дерево#.D0.9F.D0.BE.D1.81.D1.82.D1.80.D0.BE.D0.B5.D0.BD.D0.B8.D0.B5_.D0.B8.D0.B7_.D1.81.D1.83.D1.84.D1.84.D0.B8.D0.BA.D1.81.D0.BD.D0.BE.D0.B3.D0.BE_.D0.BC.D0.B0.D1.81.D1.81.D0.B8.D0.B2.D0.B0 | Построим из нового суффиксного массива дерево]], которое будет уже нечётным, что тоже делается за линейное время.
 
[[Файл:Odd.png|thumb|450px|нечетное дерево]]
 
'''Рис. 4 нечетное дерево'''
Таким образом <tex>T_{odd}</tex> может быть построено за линейное время по <tex>T_{even}</tex>.
=== Шаг 4: слияние четного и нечетного дерева ===
 
[[Файл:Tree101232merged-pre.png|thumb|450px|Слитое дерево (условно)]]
[[Файл:Tree101232merged-next.png|thumb|450px|Слитое дерево (в упрощённом виде)]]
Далее необходимо найти эффективный способ слияния нечетного и четного деревьев в одно дерево <tex>T_s</tex>. Слияние будем производить, начиная с корней деревьев.
Если начать эту процедуру для корней нечётного и чётного деревьев, она рекурсивно выполнится для корней всех поддеревьев, которые, возможно, уже содержат узлы из нечетного и четного деревьев, поскольку ранее мог быть реализован случай <tex>\lambda^{odd}</tex> <tex>=</tex> <tex>\lambda^{even}</tex>. Так как время манипулирования любым ребром этих деревьев фиксировано, то общее время слияния деревьев составит <tex>O(N)</tex>.
 
[[Файл:Tree101232merged-pre.png|thumb|450px|Слитое дерево (условно)]]
 
'''Рис. 5 Слитое дерево (условно)'''
 
 
[[Файл:Tree101232merged-next.png|thumb|450px|Слитое дерево (в упрощённом виде)]]
 
'''Рис. 6 некоторые ребра прошли процедуру слияния'''
В результате описанных действий получится дерево <tex>M_x</tex>, в котором будут присутствовать поддеревья, которые прошли процедуру слияния, и которые ее избежали (то есть были перенесены в дерево <tex>M_x</tex> без изменений).
=== Шаг 5: удаление двойных дуг ===
 
[[Файл:Tree101232merged.png|thumb|500px|откорректированное дерево]]
[[Файл:Treestep5_1.jpg|thumb|550px|пример]]
Разбираемся с двойными дугами (в примере их три). Для этого мы должны выяснить, сколько начальных символов у них совпадает. Совпадать может любое число символов, даже все. Проверять их все по очереди нельзя (это даст квадратичное время).
Если дуги совпадают полностью, тогда ничего не делаем, удаляем одну из копий и всё. Если начало для двух дуг совпадает только частично, тогда нужно делать для них общее начало, а ветки, которые на концах, снова развести по разным деревьям (для этого можно во время слияния запомнить их начальный цвет или просто сохранить ссылки на исходные ветки).
 
[[Файл:Tree101232merged.png|thumb|500px]]
 
'''Рис. 7 откорректированное дерево'''
 
Для примера как это сделать возьмём строку <tex>10010010101000</tex>:
 
[[Файл:Treestep5_1.jpg|thumb|550px]]
 
'''Рис. 8 пример'''
Для того чтобы узнать общее начало двойной дуги, нужно взять одну чётную и одну нечётную вершину на дереве, для которых родителем является конец нашей двойной дуги. Например, на рисунке выше двойная дуга <tex>(1)</tex> (конец помечен зелёным) является общим родителем для вершин <tex>3</tex> и <tex>6</tex>. Чтобы узнать, на каком расстоянии будет расслаиваться двойная дуга, надо увеличить номера вершин на единицу и найти их родителя. Он будет находиться на единицу ближе к корню (и путь у вершин будет одинаковой строкой, не считая размера). Родитель вершин <tex>4</tex> и <tex>7</tex> помечен жёлтым, он находится на расстоянии <tex>1</tex> от корня, следовательно, дуга <tex>(1)</tex> должна расслаиваться в двух символах от корня, то есть обе дуги совпадают и их просто надо слить.
[[Файл:Treestep5_2.jpg|thumb|650px|итоговое дерево]] '''Рис. 9 итоговое дерево'''
Дерево строится рекурсивно, каждый раз длина строки уменьшается в два раза, а все фазы работают линейно.
333
правки

Навигация