<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
		<id>http://neerc.ifmo.ru/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=78.25.122.106&amp;*</id>
		<title>Викиконспекты - Вклад участника [ru]</title>
		<link rel="self" type="application/atom+xml" href="http://neerc.ifmo.ru/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=78.25.122.106&amp;*"/>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%BB%D1%83%D0%B6%D0%B5%D0%B1%D0%BD%D0%B0%D1%8F:%D0%92%D0%BA%D0%BB%D0%B0%D0%B4/78.25.122.106"/>
		<updated>2026-05-12T03:35:53Z</updated>
		<subtitle>Вклад участника</subtitle>
		<generator>MediaWiki 1.30.0</generator>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%B6%D0%B0%D1%82%D0%BE%D0%B5_%D1%81%D1%83%D1%84%D1%84%D0%B8%D0%BA%D1%81%D0%BD%D0%BE%D0%B5_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE&amp;diff=46775</id>
		<title>Сжатое суффиксное дерево</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%B6%D0%B0%D1%82%D0%BE%D0%B5_%D1%81%D1%83%D1%84%D1%84%D0%B8%D0%BA%D1%81%D0%BD%D0%BE%D0%B5_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE&amp;diff=46775"/>
				<updated>2015-05-22T12:14:38Z</updated>
		
		<summary type="html">&lt;p&gt;78.25.122.106: /* Поиск строки максимальной длины, ветвящейся влево и вправо 1.4*/&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Суффиксный бор|Суффиксный бор]] {{---}} удобная структура данных для поиска подстроки в строке, но она требует порядка квадрата длины исходной строки памяти. Оптимизацией суффиксного бора, требующей линейное количество памяти, является '''сжатое суффиксное дерево''' рассматриваемое далее.&lt;br /&gt;
&lt;br /&gt;
==Определение==&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition =&lt;br /&gt;
'''Суффиксное дерево''' (сжатое суффиксное дерево) &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; для строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; (где  &amp;lt;tex&amp;gt;|s| = n&amp;lt;/tex&amp;gt;) {{---}} дерево с &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; листьями, обладающее следующими свойствами:&lt;br /&gt;
*Каждая внутренняя вершина дерева имеет не меньше двух детей;&lt;br /&gt;
*Каждое ребро помечено непустой подстрокой строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;;&lt;br /&gt;
*Никаких два ребра, выходящие из одной вершины, не могут иметь пометок, начинающихся с одного и того же символа;&lt;br /&gt;
*Дерево должно содержать все суффиксы строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;, причем каждый суффикс заканчивается точно в листе и нигде кроме него.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
[[Файл:Suffix_tree_3.png|thumb|right|Суффиксное дерево для строки &amp;lt;tex&amp;gt;xabxa&amp;lt;/tex&amp;gt; с защитным символом]] &lt;br /&gt;
'''Данное определение порождает следующую проблему:''' &amp;lt;br/&amp;gt;&lt;br /&gt;
Рассмотрим дерево для строки &amp;lt;tex&amp;gt;xabxa&amp;lt;/tex&amp;gt;: суффикс &amp;lt;tex&amp;gt;xa&amp;lt;/tex&amp;gt; является префиксом суффикса &amp;lt;tex&amp;gt;xabxa&amp;lt;/tex&amp;gt;, а, значит, этот суффикс не закачивается в листе. Для решения проблемы в конце строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; добавляют символ, не входящий в исходный алфавит: '''''защитный''''' символ. Обозначим его как &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt;. Любой суффикс строки с защитным символом действительно заканчивается в листе и только в листе, т. к. в такой строке не существует двух различных подстрок одинаковой длины, заканчивающихся на &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; {{---}} длина строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; с защитным символом.&lt;br /&gt;
&lt;br /&gt;
==Количество вершин==&lt;br /&gt;
По определению, в суффиксном дереве содержится &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; листьев. Оценим количество внутренних вершин такого дерева.&lt;br /&gt;
&lt;br /&gt;
{{Лемма&lt;br /&gt;
|statement=&lt;br /&gt;
Количество внутренних вершин дерева, каждая из которых имеет не менее двух детей, меньше количества листьев.&lt;br /&gt;
|proof=&lt;br /&gt;
Докажем лемму индукцией по количеству листьев &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
'''База'''&lt;br /&gt;
&lt;br /&gt;
При &amp;lt;tex&amp;gt;n = 2&amp;lt;/tex&amp;gt; в дереве одна внутренняя вершина, следовательно утверждение верно.&lt;br /&gt;
&lt;br /&gt;
'''Переход''' &amp;lt;tex&amp;gt;n \rightarrow n + 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Возьмем вершину в дереве с &amp;lt;tex&amp;gt;n + 1&amp;lt;/tex&amp;gt; листами, у которой два ребенка {{---}} листья. Рассмотрим возможные случаи:&lt;br /&gt;
&lt;br /&gt;
1) У нее более двух детей. Тогда отрежем от нее лист. Получим дерево с &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; листьями, причем в нем количество внутренних вершин такое же, как в исходном дереве. Но у полученного дерева по индукционному предположению менее &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; внутренних вершин, а, значит, и для исходного дерева лемма верна.&lt;br /&gt;
&lt;br /&gt;
2) У нее ровно два ребенка. Отрежем их, получим дерево с &amp;lt;tex&amp;gt;n - 1&amp;lt;/tex&amp;gt; листьями, количество внутренних вершин которого на &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; меньше, чем в исходном дереве. Тогда по индукционному предположению у него менее &amp;lt;tex&amp;gt;n - 1&amp;lt;/tex&amp;gt; внутренних вершин, значит, в исходном дереве их меньше &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Занимаемая память==&lt;br /&gt;
Представим дерево как двумерный массив размера &amp;lt;tex&amp;gt;|V| \times |\Sigma|&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;|V|&amp;lt;/tex&amp;gt; {{---}} количество вершин в дереве, &amp;lt;tex&amp;gt;|\Sigma|&amp;lt;/tex&amp;gt; {{---}} мощность алфавита. Для любого суффиксного дерева верна предыдущая лемма (у каждой вершины, по определению, не менее двух детей), значит, &amp;lt;tex&amp;gt;|V| = O(2 n)&amp;lt;/tex&amp;gt;. Каждая &amp;lt;tex&amp;gt;[i][j]&amp;lt;/tex&amp;gt; ячейка содержит информацию о том, в какую вершину ведет ребро из &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-ой вершины по &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-ому символу и индексы &amp;lt;tex&amp;gt;l, r&amp;lt;/tex&amp;gt; начала и конца подстроки, записанной на данном переходе. Итак, дерево занимает &amp;lt;tex&amp;gt;O(n|\Sigma|)&amp;lt;/tex&amp;gt; памяти.&lt;br /&gt;
&lt;br /&gt;
==Построение суффиксного дерева==&lt;br /&gt;
&lt;br /&gt;
===Наивный алгоритм===&lt;br /&gt;
Рассмотрим наивный алгоритм построения суффиксного дерева строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;:&lt;br /&gt;
 go[0] = Vertex() // корень&lt;br /&gt;
 count = 0 // номер последней вершины, созданной в дереве (глобальная переменная)&lt;br /&gt;
 '''for''' i = 0 '''to''' n // для каждого символа строки&lt;br /&gt;
     insert(i, n) // добавляем суффикс, начинающийся с него&lt;br /&gt;
&lt;br /&gt;
 insert(l, r):&lt;br /&gt;
     cur = 0 &lt;br /&gt;
     '''while''' l &amp;lt; r&lt;br /&gt;
         '''if''' go[cur][s[l]].v == -1        // если мы не можем пойти из вершины по символу &amp;lt;tex&amp;gt; l &amp;lt;/tex&amp;gt;&lt;br /&gt;
             createVertex(cur, l, r)     // создаем новую вершину &lt;br /&gt;
         '''else'''&lt;br /&gt;
             start = go[cur][s[l]].l&lt;br /&gt;
             finish = go[cur][s[l]].r&lt;br /&gt;
             hasCut = false&lt;br /&gt;
             '''for''' j = start '''to''' finish     // для каждого символа на ребре из текущей вершины&lt;br /&gt;
                 '''if''' s[l+j-start] &amp;lt;tex&amp;gt; \neq &amp;lt;/tex&amp;gt; s[j] // если нашли не совпадающий символ&lt;br /&gt;
                     // создаем вершину на ребре&lt;br /&gt;
                     old = go[cur][s[l]]&lt;br /&gt;
                     createVertex(cur, l, j - 1)&lt;br /&gt;
                     go[count][s[j]].v = old&lt;br /&gt;
                     go[count][s[j]].r = j&lt;br /&gt;
                     go[count][s[j]].l = finish&lt;br /&gt;
                     createVertex(count, l + j - start, r)&lt;br /&gt;
                     hasCut = true&lt;br /&gt;
                     '''break'''&lt;br /&gt;
             '''if''' !hasCut&lt;br /&gt;
                 cur = go[cur][s[l]].v  // переходим по ребру&lt;br /&gt;
                 l = l + finish - start // двигаемся по суффиксу на длину подстроки, записанной на ребре&lt;br /&gt;
             '''else'''&lt;br /&gt;
                 '''break'''&lt;br /&gt;
&lt;br /&gt;
 createVertex(cur, l, r):&lt;br /&gt;
     go[++count] = Vertex()&lt;br /&gt;
     go[cur][s[l]].v = count&lt;br /&gt;
     go[cur][s[l]].l = l&lt;br /&gt;
     go[cur][s[l]].r = r&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Этот алгоритм работает за время &amp;lt;tex&amp;gt;O(n^2)&amp;lt;/tex&amp;gt;, однако [[Алгоритм Укконена| алгоритм Укконена]] позволяет построить сжатое суффиксное дерево за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Построение из суффиксного массива===&lt;br /&gt;
Пусть нам известен [[Суффиксный массив| суффиксный массив]] &amp;lt;tex&amp;gt;suf&amp;lt;/tex&amp;gt; строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;, его можно получить [[Алгоритм Карккайнена-Сандерса| алгоритмом Карккайнена-Сандерса]] за линейное время. Для преобразования нам также понадобится массив &amp;lt;tex&amp;gt;lcp&amp;lt;/tex&amp;gt; (longest common prefix), который можно получить [[Алгоритм Касаи и др.| алгоритмом Касаи]].&lt;br /&gt;
&lt;br /&gt;
В этом преобразовании используется тот же инвариант, что и в других суффиксных структурах:&lt;br /&gt;
# Строка &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; заканчивается специальным символом, который больше не встречается в строке.&lt;br /&gt;
# Следствие: &amp;lt;tex&amp;gt;lcp[i] &amp;lt; len[i - 1]&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;len[i - 1]&amp;lt;/tex&amp;gt; {{---}} длина суффикса, соответствующего &amp;lt;tex&amp;gt;suf[i - 1]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Будем строить дерево, добавляя суффиксы в лексикографическом порядке. Чтобы ускорить добавление, будем использовать то, что &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-ый суффикс имеет с предыдущим &amp;lt;tex&amp;gt;lcp[i]&amp;lt;/tex&amp;gt; общих символов. Тогда добавление из корня не отличается от того, что мы поднимемся вверх из предыдущего суффикса до глубины &amp;lt;tex&amp;gt;lcp[i]&amp;lt;/tex&amp;gt; и продолжим построение оттуда. Инвариант позволяет нам утверждать, что ни один лист мы не сможем продолжить, и нам всегда нужно будет хоть раз подняться из него вверх. Поскольку суффиксы отсортированы лексикографически, мы не будем спускаться по ребру после того, как уже поднялись из него из-за несовпадения символа. Все это позволяет сформулировать алгоритм добавления суффикса по известной вершине предыдущего суффикса:&lt;br /&gt;
# Подняться из вершины вверх до глубины &amp;lt;tex&amp;gt;lcp&amp;lt;/tex&amp;gt;&lt;br /&gt;
# Если эта глубина находится на ребре, разрезать ребро по ней.&lt;br /&gt;
# Вставить новую вершину как сына вершины с глубиной &amp;lt;tex&amp;gt;lcp&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В вершинах дерева &amp;lt;tex&amp;gt;Node&amp;lt;/tex&amp;gt; мы будем хранить предка &amp;lt;tex&amp;gt;parent&amp;lt;/tex&amp;gt;, [[Стек| стек]] детей в лексикографическом порядке ребер &amp;lt;tex&amp;gt;children&amp;lt;/tex&amp;gt;, глубину вершины в символах от корня &amp;lt;tex&amp;gt;depth&amp;lt;/tex&amp;gt;. &lt;br /&gt;
Соответственно, конструктор вершины имеет вид &amp;lt;code&amp;gt;Node(Node parent, '''int''' depth)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 Node addNextSuffix(Node previous, '''int''' length, '''int''' lcp):&lt;br /&gt;
    '''if''' previous.depth == 0 '''or''' previous.depth == lcp           &amp;lt;font color=green&amp;gt; // Добавляем к сыновьям текущей вершины &amp;lt;/font&amp;gt;&lt;br /&gt;
       added = Node(previous, length)&lt;br /&gt;
       previous.children.push(added)&lt;br /&gt;
       '''return''' added&lt;br /&gt;
    '''else'''&lt;br /&gt;
       '''if''' previous.parent.depth &amp;lt; lcp:                         &amp;lt;font color=green&amp;gt; // Нужно разрезать ребро &amp;lt;/font&amp;gt;&lt;br /&gt;
          inserted = Node(prevous.parent, lcp)&lt;br /&gt;
          previous.parent.children.pop()&lt;br /&gt;
          previous.parent.children.push(inserted)&lt;br /&gt;
          inserted.children.push(previous)&lt;br /&gt;
          previous.parent = inserted&lt;br /&gt;
       '''return''' addNextSuffix(previous.parent, length, lcp)      &lt;br /&gt;
       &lt;br /&gt;
 Node buildSuffixTree('''int[]''' suf, '''int[]''' lcp, '''int''' length):&lt;br /&gt;
    root = Node('''null''', 0)&lt;br /&gt;
    previous = root&lt;br /&gt;
    '''for''' i = 1 '''to''' length&lt;br /&gt;
       previous = addNextSuffix(previous, length - suf[i], lcp[i])&lt;br /&gt;
    '''return''' root&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
В процессе построения мы нигде не запоминали сами позиции строки, соответствующие ребрам. Чтобы их восстановить, достаточно определить максимальный суффикс, который проходит по этому ребру. Для этого с помощью [[Обход в глубину, цвета вершин| обхода в глубину]] посчитаем для каждой вершину дерева максимальную глубину ее листа &amp;lt;tex&amp;gt;maxDepth&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Тогда ребро &amp;lt;tex&amp;gt;s[start, end]&amp;lt;/tex&amp;gt; определяется так:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 '''function''' calculatePositions(Node parent, Node child, '''int''' stringLength):&lt;br /&gt;
    start = stringLength - child.maxDepth + parent.depth&lt;br /&gt;
    end = start + child.depth - parent.depth - 1&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Для асимптотического анализа будем использовать в качестве [[Амортизационный анализ#Метод потенциалов| потенциала]] глубину в вершинах. При добавлении суффикса мы спускаемся один раз, подняться выше корня мы не можем, значит, и подниматься мы будем суммарно &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; раз. Обход в глубину также выполняется за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;, итоговая асимптотика &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Использование сжатого суффиксного дерева==&lt;br /&gt;
Суффиксное дерево позволяет за линейное время найти:&lt;br /&gt;
* Количество различных подстрок данной строки&lt;br /&gt;
* Наибольшую общую подстроку двух строк&lt;br /&gt;
* [[Суффиксный массив| Суффиксный массив]] и массив &amp;lt;tex&amp;gt;lcp&amp;lt;/tex&amp;gt; (longest common prefix) исходной строки&lt;br /&gt;
* Строку максимальной длины, ветвящуюся влево и вправо за &amp;lt;tex&amp;gt;ST + O(n)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Построение суффиксного массива и массива lcp из суффиксного дерева===&lt;br /&gt;
Пусть к строке дописан специальный символ для сохранения инварианта.&lt;br /&gt;
Рассмотрим лексикографический по ребрам порядок обхода сжатого суффиксного дерева. &lt;br /&gt;
Пусть два суффикса имеют общее начало, но различаются в &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-ом символе. &lt;br /&gt;
Первым будет рассмотрено поддерево по ребру с меньшим символом, значит и лист, соответствующий этому суффиксу, будет посещен первым.&lt;br /&gt;
&lt;br /&gt;
Тогда суффиксный массив строится из суффиксного дерева [[Обход в глубину, цвета вершин| обходом в глубину]] в указанном порядке. &lt;br /&gt;
Пусть длина строки &amp;lt;tex&amp;gt;length&amp;lt;/tex&amp;gt;, глубина листа в символах &amp;lt;tex&amp;gt;depth&amp;lt;/tex&amp;gt;, тогда номер суффикса &amp;lt;tex&amp;gt;i = length - depth&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для заполнения массива &amp;lt;tex&amp;gt;lcp&amp;lt;/tex&amp;gt; нам понадобится вершина &amp;lt;tex&amp;gt;minNode&amp;lt;/tex&amp;gt;, которая будет означать вершину с минимальной глубиной, в которую мы поднимались при переходе между суффиксами. Поскольку мы точно поднимались туда, но не поднимались выше, это будет [[Сведение задачи LCA к задаче RMQ| наименьший общий предок]] этих узлов. Из этого следует, что у рассматриваемых суффиксов совпадает ровно &amp;lt;tex&amp;gt;lcp = minNode.depth&amp;lt;/tex&amp;gt; символов.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 '''int''' curPos = 0&lt;br /&gt;
 Node minNode = root&lt;br /&gt;
 &amp;lt;font color=green&amp;gt;// Для заполнения нужно вызвать dfs(root) &amp;lt;/font&amp;gt;&lt;br /&gt;
 '''function''' dfs(Node n):&lt;br /&gt;
    '''if''' n.children.size == 0&lt;br /&gt;
       suf[curPos] = length - n.depth&lt;br /&gt;
       lcp[curPos] = minNode.depth&lt;br /&gt;
       curPos++&lt;br /&gt;
       minNode = n&lt;br /&gt;
    '''else'''&lt;br /&gt;
       '''foreach''' child '''in''' n.children&lt;br /&gt;
          '''if''' n.depth &amp;lt; minNode.depth:&lt;br /&gt;
             minNode = n&lt;br /&gt;
          dfs(child)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Асимптотика алгоритма совпадает с асимптотикой обхода в глубину и составляет &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Таким образом, мы умеем за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; строить [[Алгоритм Укконена| суффиксное дерево]], [[Алгоритм Карккайнена-Сандерса| суффиксный массив]] и преобразовывать одно в другое.&lt;br /&gt;
&lt;br /&gt;
===Поиск строки максимальной длины, ветвящейся влево и вправо===&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Строка &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; называется ветвящейся вправо в &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;''' (англ. ''right branching string''), если существуют символы &amp;lt;tex&amp;gt;c&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;, такие что &amp;lt;tex&amp;gt;c&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\ne&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; : &amp;lt;tex&amp;gt;sc&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;sd&amp;lt;/tex&amp;gt; {{---}} подстроки &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;. Аналогично, '''ветвящаяся влево''' (англ. ''left branching''), если &amp;lt;tex&amp;gt;cs&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;ds&amp;lt;/tex&amp;gt; {{---}} подстроки &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
[[Файл:RightMergingSS.png|thumb|400px|center|Суффиксное дерево для строки &amp;lt;tex&amp;gt;aabcabd&amp;lt;/tex&amp;gt;]]&lt;br /&gt;
Построим cуффиксное дерево при помощи [[Алгоритм Укконена|алгоритма Укконена]]. В полученном дереве не листовой вершине &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; будет соответствовать подстрока &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;, которая ветвится вправо, при условии, что количество &amp;quot;хороших&amp;quot; детей вершины &amp;lt;tex&amp;gt;v &amp;gt; 2&amp;lt;/tex&amp;gt; (&amp;quot;хорошие&amp;quot; дети {{---}} листы, метка которых &amp;lt;tex&amp;gt;\ne\$&amp;lt;/tex&amp;gt;). В примере для строки &amp;lt;tex&amp;gt;aabcabd&amp;lt;/tex&amp;gt; это &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;ab&amp;lt;/tex&amp;gt;. Далее введём термины ''левый символ'' и ''вершина различная влево'', чтобы найти строки, ветвящиеся влево.&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Левый символ''' для позиции &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; строки &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; {{---}} это символ &amp;lt;tex&amp;gt;S(i-1)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
'''Левым символом''' листа &amp;lt;tex&amp;gt;L&amp;lt;/tex&amp;gt; называется ''левый символ'' начала суффикса, ведущего в этот лист.&lt;br /&gt;
}}&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Вершина &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; различна влево''', если как минимум два листа в поддереве &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; имеют различные ''левые символы''. По определению лист не может быть различным влево.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Допустим, что строка ветвится влево. Пусть существуют подстроки &amp;lt;tex&amp;gt;cs&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;ds&amp;lt;/tex&amp;gt;, тогда есть два суффикса, начинающиеся с &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;, которым соответствуют различные листовые вершины с различными левыми символами (&amp;lt;tex&amp;gt;c&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;). Также в дереве существует внутренняя вершина &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; соответствующая строке &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;, по определению получаем, что &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; различна влево.&lt;br /&gt;
&lt;br /&gt;
Чтобы найти строки, ветвящиеся влево, нужно проверить все вершины суффиксного дерева на различность влево. Если какая-то вершина &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; будет различна влево и удовлетворять свойству ветвимости право, то строка, соответствующая вершине &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; будет ветвится вправо и влево.&lt;br /&gt;
&lt;br /&gt;
Чтобы найти вершины различные влево, будем хранить левый символ для каждой вершины или информацию о том, что она является левой. Для поиска строки, ветвящейся влево, нужно промаркировать всё дерево. Сделать это можно при помощи [[Обход в глубину, цвета вершин| обхода в глубину]]. Начиная с корня, будем спускаться вниз, для листов будем записывать левый символ суффикса, соответствующего данному листу, для вершины &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; будем запускать проверку:&lt;br /&gt;
*если среди левых детей &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; есть хотя бы один, удовлетворяющий свойству различности влево, то обозначим &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; как различную влево вершину и закончим проверку (в суффиксном дереве свойство различности влево передаётся от детей к родителю {{---}} строка соответствующая вершине &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; и строка соответствующая ребёнку &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; начинаются с одного и того же символа),&lt;br /&gt;
*если среди левых символов детей &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; нет ни одного со свойством различная влево вершина, то проверим на совпадение левые символы детей:&lt;br /&gt;
**если все левые символ детей &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; одинаковы и эквивалентны &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;, то запишем в &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; этот символ &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;,&lt;br /&gt;
**если не все левые символы детей &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; эквивалентны, то запишем в &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, что она различна влево.&lt;br /&gt;
&lt;br /&gt;
Так как время проверки &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; пропорционально числу детей, время работы всего алгоритма {{---}} &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Далее соберём все строки удовлетворяющие условию теоремы и найдём среди них максимальную (также этот алгоритм можно использовать для нахождения количества строк, ветвящихся влево и вправо).  &lt;br /&gt;
&lt;br /&gt;
Таким образом можно найти строку максимальной длины, ветвящуюся влево и вправо за &amp;lt;tex&amp;gt;ST+O(n)&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;ST&amp;lt;/tex&amp;gt; {{---}} время построения суффиксного дерева.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
* [[Суффиксный бор|Суффиксный бор]]&lt;br /&gt;
* [[Суффиксный массив| Суффиксный массив]]&lt;br /&gt;
* [[Алгоритм Укконена| Алгоритм Укконена]] &lt;br /&gt;
&lt;br /&gt;
==Источники информации==&lt;br /&gt;
*''Дэн Гасфилд'' — '''Строки, деревья и последовательности в алгоритмах: Информатика и вычислительная биология''' — СПб.: Невский Диалект; БХВ-Петербург, 2003. — 654 с: ил.&lt;br /&gt;
&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Словарные структуры данных ]]&lt;/div&gt;</summary>
		<author><name>78.25.122.106</name></author>	</entry>

	</feed>