<?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=66.249.81.157&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=66.249.81.157&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/66.249.81.157"/>
		<updated>2026-04-09T12:43:16Z</updated>
		<subtitle>Вклад участника</subtitle>
		<generator>MediaWiki 1.30.0</generator>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A3%D0%BA%D0%BA%D0%BE%D0%BD%D0%B5%D0%BD%D0%B0&amp;diff=45449</id>
		<title>Алгоритм Укконена</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%A3%D0%BA%D0%BA%D0%BE%D0%BD%D0%B5%D0%BD%D0%B0&amp;diff=45449"/>
				<updated>2015-04-13T07:07:08Z</updated>
		
		<summary type="html">&lt;p&gt;66.249.81.157: /* Алгоритм за O(n3) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Алгоритм Укконена''' (англ. ''Ukkonen's algorithm'') — алгоритм построения [[Сжатое суффиксное дерево|суффиксного дерева]] для заданной строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; за линейное время.&lt;br /&gt;
&lt;br /&gt;
== Алгоритм за O(n&amp;lt;sup&amp;gt;3&amp;lt;/sup&amp;gt;) ==&lt;br /&gt;
Рассмотрим сначала наивный метод, который строит дерево за время &amp;lt;tex&amp;gt;O(n^3)&amp;lt;/tex&amp;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;
|definition= '''Неявное суффиксное дерево''' (англ. ''implicit suffix tree, IST'') строки &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; {{---}} это суффиксное дерево, построенное для строки &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt; без добавления защитного символа.}}&lt;br /&gt;
[[Файл:ExampleUkkonen2.png|400px|thumb|right|Пример построения суффиксного дерева алгоритмом Укконена.]]&lt;br /&gt;
Алгоритм последовательно строит неявные суффиксные деревья для всех префиксов исходного текста &amp;lt;tex&amp;gt;S = s_{1}s_{2}...s_{n}&amp;lt;/tex&amp;gt;. На &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-ой итерации неявное суффиксное дерево &amp;lt;tex&amp;gt;\tau_{i-1}&amp;lt;/tex&amp;gt; для префикса &amp;lt;tex&amp;gt;s[1..i-1]&amp;lt;/tex&amp;gt; достраивается до &amp;lt;tex&amp;gt;\tau_{i}&amp;lt;/tex&amp;gt; для префикса &amp;lt;tex&amp;gt;s[1..i]&amp;lt;/tex&amp;gt;. Будем спускаться от корня дерева до конца каждого суффикса префикса &amp;lt;tex&amp;gt;s[1..i-1]&amp;lt;/tex&amp;gt; и дописывать к ним символ &amp;lt;tex&amp;gt;s_{i}&amp;lt;/tex&amp;gt;. Не стоит забывать,  что &amp;lt;tex&amp;gt;s_{i}&amp;lt;/tex&amp;gt; является суффиксом &amp;lt;tex&amp;gt;s[1..i]&amp;lt;/tex&amp;gt; , поэтому его тоже нужно добавить в дерево. &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Алгоритм состоит из &amp;lt;tex&amp;gt;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^2)&amp;lt;/tex&amp;gt; времени. Следовательно, общая асимптотика алгоритма &amp;lt;tex&amp;gt;O(n^3)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Продление суффиксов ==&lt;br /&gt;
Ниже приведены возможные случаи, которые могут возникнуть при добавлении символа &amp;lt;tex&amp;gt;s_{i}&amp;lt;/tex&amp;gt; ко всем суффиксам префикса &amp;lt;tex&amp;gt;s[1..i-1]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;5&amp;quot; cellspacing=&amp;quot;0&amp;quot; style=&amp;quot;text-align:center&amp;quot; width=75%&lt;br /&gt;
!style=&amp;quot;background:#f2f2f2&amp;quot;|Случай&lt;br /&gt;
!style=&amp;quot;background:#f2f2f2&amp;quot;|Правило&lt;br /&gt;
!style=&amp;quot;background:#f2f2f2&amp;quot;|Пример&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background:#ffffff&amp;quot;|''1. Продление листа''&lt;br /&gt;
|style=&amp;quot;background:#ffffff&amp;quot;|Пусть суффикс &amp;lt;tex&amp;gt;s[k..i-1]&amp;lt;/tex&amp;gt; заканчивается в листе. Добавим &amp;lt;tex&amp;gt;s_{i}&amp;lt;/tex&amp;gt; в конец подстроки, которой помечено ребро, ведущее в этот лист.&lt;br /&gt;
|style=&amp;quot;background:#ffffff&amp;quot;|[[Файл:ExampleUkkonen3.png|300px]]&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background:#ffffff&amp;quot;|''2.1 Создание листа''&lt;br /&gt;
|style=&amp;quot;background:#ffffff&amp;quot;|Пусть суффикс &amp;lt;tex&amp;gt;s[k..i-1]&amp;lt;/tex&amp;gt; заканчивается в вершине, не являющейся листом, из которой нет пути по символу &amp;lt;tex&amp;gt;s_{i}&amp;lt;/tex&amp;gt;. Создадим новую дугу с началом в элементе &amp;lt;tex&amp;gt;s[i-1]&amp;lt;/tex&amp;gt; и листом &amp;lt;tex&amp;gt;s_{i}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|style=&amp;quot;background:#ffffff&amp;quot;|[[Файл:ExampleUkkonen4.png|300px]]&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background:#ffffff&amp;quot;|''2.2 Ответвление''&lt;br /&gt;
|style=&amp;quot;background:#ffffff&amp;quot;|Пусть суффикс &amp;lt;tex&amp;gt;s[k..i-1]&amp;lt;/tex&amp;gt; заканчивается на ребре, &amp;lt;tex&amp;gt;t[1..p-1]&amp;lt;/tex&amp;gt; совпадает с концом &amp;lt;tex&amp;gt;s[k..i-1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t_{p}\ne s_{i}&amp;lt;/tex&amp;gt;. Разобьем текущее ребро новой вершиной на &amp;lt;tex&amp;gt;t[1..p-1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;t[p..l]&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt; {{---}} длина метки ребра, и подвесим к ней еще одного ребенка с дугой, помеченной &amp;lt;tex&amp;gt;s_{i}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|style=&amp;quot;background:#ffffff&amp;quot;|[[Файл:ExampleUkkonen5.png|300px]]&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background:#ffffff&amp;quot;|''3 Ничего не делать''&lt;br /&gt;
|style=&amp;quot;background:#ffffff&amp;quot;|Пусть суффикс &amp;lt;tex&amp;gt;s[k..i-1]&amp;lt;/tex&amp;gt; заканчивается в вершине, из которой есть путь по &amp;lt;tex&amp;gt;s_{i}&amp;lt;/tex&amp;gt;. Тогда ничего делать не надо.&lt;br /&gt;
|style=&amp;quot;background:#ffffff&amp;quot;|[[Файл:ExampleUkkonen6.png|300px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Суффиксные ссылки==&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition= Пусть &amp;lt;tex&amp;gt;x\alpha&amp;lt;/tex&amp;gt; обозначает произвольную строку, где &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; {{---}} ее первый символ, а &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt; {{---}} оставшаяся подстрока(возможно пустая). Если для внутренней вершины &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; с путевой меткой &amp;lt;tex&amp;gt;x\alpha&amp;lt;/tex&amp;gt; существует другая вершина &amp;lt;tex&amp;gt;s(v)&amp;lt;/tex&amp;gt;  с путевой меткой &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt;, то ссылка из &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;s(v)&amp;lt;/tex&amp;gt; называется '''суффиксной ссылкой'''.}}&lt;br /&gt;
&lt;br /&gt;
{{Лемма|id=l3&lt;br /&gt;
|about= Существование суффиксных ссылок&lt;br /&gt;
|statement=&lt;br /&gt;
Для любой внутренней вершины &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; суффиксного дерева существует суффиксная ссылка, ведущая в некоторую внутреннюю вершину &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим внутренную вершину &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; с путевой меткой &amp;lt;tex&amp;gt;t[i..j]&amp;lt;/tex&amp;gt;. Так как эта вершина внутренняя, ее путевая метка ветвится справа в исходной строке. Тогда очевидно подстрока &amp;lt;tex&amp;gt;t[i+1..j]&amp;lt;/tex&amp;gt; тоже ветвится справа в исходной строке, и ей соответствует некоторая внутренняя вершина &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;. По определению суффиксная ссылка вершины &amp;lt;tex&amp;gt;v &amp;lt;/tex&amp;gt; ведет в &amp;lt;tex&amp;gt; u&amp;lt;/tex&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== Использование суффиксных ссылок ===&lt;br /&gt;
[[Файл:ExampleUkkonen7.png|200px|thumb|right|Иллюстрация использования суффиксных ссылок.]]&lt;br /&gt;
Суффиксные ссылки используются для того, чтобы можно было быстро перейти от конца одного суффикса к концу другого, а не спускаться каждый раз от корня. Пусть мы только что продлили суффикс &amp;lt;tex&amp;gt;s[l..i-1]&amp;lt;/tex&amp;gt; до суффикса &amp;lt;tex&amp;gt;s[l..i]&amp;lt;/tex&amp;gt; и стоим в вершине, в которую ведет ребро с пометкой &amp;lt;tex&amp;gt;t[k+1..r]&amp;lt;/tex&amp;gt;, содержащей конец текущего суффикса. Найдем с помощью построенных ссылок конец суффикса &amp;lt;tex&amp;gt;s[l+1..i-1]&amp;lt;/tex&amp;gt;. Пройдем вверх по дереву до ближайшей внутренней вершины &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;, в которую ведет ребро с пометкой &amp;lt;tex&amp;gt;t[p..k]&amp;lt;/tex&amp;gt;. У вершины &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; есть суффиксная ссылка, так как ссылка для новой внутренней вершины строится внутри фазы ее создания. Пусть суффиксная ссылка ведет в вершину &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, которой соответствует пометка &amp;lt;tex&amp;gt;t[h..k]&amp;lt;/tex&amp;gt; (&amp;lt;tex&amp;gt;h&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt; могут быть не равны). Теперь пройдем от вершины &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; вниз по дереву к концу суффикса &amp;lt;tex&amp;gt;s[l+1..i-1]&amp;lt;/tex&amp;gt;, и сделаем продление до суффикса &amp;lt;tex&amp;gt;s[l+1..i]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Построение суффиксных ссылок ===&lt;br /&gt;
&lt;br /&gt;
Заметим что в процессе построения суффиксного дерева уже построенные суффиксные ссылки никак не изменяются. Опишем процесс построения суффиксной ссылки для новой созданной внутренней вершины. Пусть в результате очередного продления была создана новая внутренняя вершина &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; с путевой меткой &amp;lt;tex&amp;gt;t[l..r]&amp;lt;/tex&amp;gt;. Не будем специально искать, куда должна указывать ссылка. Перейдем к следующему шагу текущей фазы, на котором в дерево будет добавлен суффикс &amp;lt;tex&amp;gt;s[j+1..i]&amp;lt;/tex&amp;gt;. Этот суффикс может так же оканчиваться на ребре, но тогда будет создана новая внутренняя вершина &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;, по определению суффиксная ссылка из вершины &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; ведет в &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Оценка числа переходов ===&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition= '''Глубиной вершины''' &amp;lt;tex&amp;gt;d(v)&amp;lt;/tex&amp;gt; назовем число ребер на пути от корня до вершины &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;}}&lt;br /&gt;
&lt;br /&gt;
{{Лемма|id=l4&lt;br /&gt;
|statement=&lt;br /&gt;
При переходе по суффиксной ссылке глубина уменьшается не более чем на &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof= &lt;br /&gt;
Пусть мы переходим из вершины &amp;lt;tex&amp;gt; v &amp;lt;/tex&amp;gt; с путевой меткой &amp;lt;tex&amp;gt;t[i..j]&amp;lt;/tex&amp;gt; по суффиксной ссылке в вершину &amp;lt;tex&amp;gt; u &amp;lt;/tex&amp;gt; с путевой меткой &amp;lt;tex&amp;gt;t[i+1..j]&amp;lt;/tex&amp;gt; Определим множество &amp;lt;tex&amp;gt; A &amp;lt;/tex&amp;gt; как множество вершин на пути от корня до &amp;lt;tex&amp;gt; u &amp;lt;/tex&amp;gt;, исключая корень. Множество &amp;lt;tex&amp;gt; B &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;B&amp;lt;/tex&amp;gt; вершину, в которую ведет это ребро. Итого по построению получаем: &amp;lt;tex&amp;gt;|A| = d(u)&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;|B| \ge d(v) - 1&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;|A| \ge |B|&amp;lt;/tex&amp;gt;, а значит &amp;lt;tex&amp;gt;d(u) \ge d(v) - 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Лемма|id=l5&lt;br /&gt;
|statement=&lt;br /&gt;
Число переходов по ребрам внутри фазы номер &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; не превышает &amp;lt;tex&amp;gt;4i&amp;lt;/tex&amp;gt;&lt;br /&gt;
|proof=&lt;br /&gt;
Оценим количество переходов по ребрам при поиске конца суффикса. Переход до ближайшей внутренней вершины уменьшает высоту на &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;. Переход по суффиксной ссылке уменьшает высоту не более чем на &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;  (по лемме, доказанной выше). Значит в течение одной фазы вверх мы переходим не более &amp;lt;tex&amp;gt;2i&amp;lt;/tex&amp;gt; раз. Но внутри одной фазы начальная глубина не меньше конечной (так как длины суффиксов убывают до &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;), поэтому вниз мы могли пройти не более &amp;lt;tex&amp;gt;2i&amp;lt;/tex&amp;gt; ребер. Итого получаем оценку &amp;lt;tex&amp;gt;4i&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;O(n^2)&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt;, так как по доказанной выше лемме на каждом шаге мы делаем не более O(n) переходов. Следовательно, общая асимптотика алгоритма улучшилась до &amp;lt;tex&amp;gt;O(n^2)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Линейный алгоритм==&lt;br /&gt;
&lt;br /&gt;
{{Лемма|id=l1&lt;br /&gt;
|about= Стал листом — листом и останешься&lt;br /&gt;
|statement=&lt;br /&gt;
Если в какой-то момент работы алгоритма Укконена будет создан лист с меткой &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;  (для суффикса, начинающегося в позиции &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; строки &amp;lt;tex&amp;gt;S&amp;lt;/tex&amp;gt;), он останется листом во всех последовательных деревьях, созданных алгоритмом. &lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
|proof=&lt;br /&gt;
Это верно потому, что у алгоритма нет механизма продолжения листового ребра дальше текущего листа. Если есть лист с суффиксом &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, правило продолжения 1 будет применяться для продолжения &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; на всех последующих фазах.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Лемма|id=l2&lt;br /&gt;
|about= Правило 3 заканчивает дело&lt;br /&gt;
|statement=&lt;br /&gt;
В любой фазе, если правило продления 3 применяется в продолжении &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, оно будет реализовываться во всех дальнейших продолжениях (от &amp;lt;tex&amp;gt;i+1&amp;lt;/tex&amp;gt; по &amp;lt;tex&amp;gt;j+1&amp;lt;/tex&amp;gt;) до конца фазы. &lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
|proof=&lt;br /&gt;
При использовании правила продолжения 3 путь, помеченный &amp;lt;tex&amp;gt;s[i..j]&amp;lt;/tex&amp;gt; в текущем дереве, должен продолжаться символом &amp;lt;tex&amp;gt;j+1&amp;lt;/tex&amp;gt;, и точно так же продолжается путь, помеченный &amp;lt;tex&amp;gt;s[i+1..j]&amp;lt;/tex&amp;gt;, поэтому правило 3 применяется в продолжениях &amp;lt;tex&amp;gt;i+1, i+2, ..., j+1&amp;lt;/tex&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Когда используется 3-е правило продления суффикса, никакой работы делать не нужно, так как требуемый суффикс уже в дереве есть. Поэтому можно заканчивать текущую итерацию после первого же использования этого правила. Так как лист навсегда останется листом, зададим метку ребра ведущего в этот лист как &amp;lt;tex&amp;gt;t[i..x]&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;{{---}} ссылка на переменную, хранящую конец текущей подстроки. На следующих итерациях к этому ребру может применяться правило ответвления, но при этом будет меняться только левый(начальный) индекс &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;. Таким образом мы сможем удлинять все суффиксы, заканчивающиеся в листах за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
=== Итоговая оценка времени работы ===&lt;br /&gt;
&lt;br /&gt;
Все неявные продления листов суммарно можно выполнить за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; ([[#l1|по Лемме 1]]). [[#l2|По Лемме 2]] алгоритм делает не более &amp;lt;tex&amp;gt;2n&amp;lt;/tex&amp;gt; явных продлений. Таким образом, в течение всех &amp;lt;tex&amp;gt;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;
  &amp;lt;font color=green&amp;gt;// &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; {{---}} исходный текст&amp;lt;/font&amp;gt;&lt;br /&gt;
  &amp;lt;font color=green&amp;gt;// &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; {{---}} длина текста&amp;lt;/font&amp;gt;&lt;br /&gt;
  &amp;lt;font color=green&amp;gt;// &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; {{---}} массив, в котором хранится дерево&amp;lt;/font&amp;gt;&lt;br /&gt;
  &amp;lt;font color=green&amp;gt;// &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt; {{---}} размер суффиксного дерева&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  '''struct node''' &lt;br /&gt;
    &amp;lt;tex&amp;gt;l, r, par,link&amp;lt;/tex&amp;gt;&lt;br /&gt;
    &amp;lt;tex&amp;gt;\mathtt{next}[]&amp;lt;/tex&amp;gt;&lt;br /&gt;
    '''function''' &amp;lt;tex&amp;gt;\mathtt{len}():&amp;lt;/tex&amp;gt;&lt;br /&gt;
      '''return''' &amp;lt;tex&amp;gt;r - l&amp;lt;/tex&amp;gt;&lt;br /&gt;
    '''function''' &amp;lt;tex&amp;gt;\mathtt{get}(c):&amp;lt;/tex&amp;gt;&lt;br /&gt;
      '''if''' &amp;lt;tex&amp;gt;!next.count(c)&amp;lt;/tex&amp;gt;&lt;br /&gt;
        &amp;lt;tex&amp;gt;\mathtt{next}[c] = -1&amp;lt;/tex&amp;gt;&lt;br /&gt;
      '''return''' &amp;lt;tex&amp;gt;\mathtt{next}[c]&amp;lt;/tex&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  '''struct state'''&lt;br /&gt;
    &amp;lt;tex&amp;gt;v, pos&amp;lt;/tex&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
  '''state''' &amp;lt;tex&amp;gt;ptr(0, 0)&amp;lt;/tex&amp;gt; &amp;lt;font color=green&amp;gt;// указатель на конец самого длинного не уникального суффикса&amp;lt;/font&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  '''function''' &amp;lt;tex&amp;gt;\mathtt{go}(st, l, r):&amp;lt;/tex&amp;gt;&lt;br /&gt;
    '''while''' &amp;lt;tex&amp;gt;l &amp;lt; r&amp;lt;/tex&amp;gt;&lt;br /&gt;
      '''if''' &amp;lt;tex&amp;gt;st.pos == \mathtt{t}[st.v].\mathtt{len}()&amp;lt;/tex&amp;gt;&lt;br /&gt;
        &amp;lt;tex&amp;gt;st = \mathtt{state}(\mathtt{t}[st.v].\mathtt{get}(\mathtt{s}[l]), 0)&amp;lt;/tex&amp;gt;&lt;br /&gt;
        '''if''' &amp;lt;tex&amp;gt;st.v == -1&amp;lt;/tex&amp;gt;&lt;br /&gt;
          '''return''' &amp;lt;tex&amp;gt;st&amp;lt;/tex&amp;gt;&lt;br /&gt;
      '''else'''&lt;br /&gt;
        '''if''' &amp;lt;tex&amp;gt;\mathtt{s}[\mathtt{t}[st.v].l + st.pos] \ne \mathtt{s}[l]&amp;lt;/tex&amp;gt;&lt;br /&gt;
          '''return''' &amp;lt;tex&amp;gt;\mathtt{state}(-1, -1)&amp;lt;/tex&amp;gt;&lt;br /&gt;
        '''if''' &amp;lt;tex&amp;gt;r - l &amp;lt; \mathtt{t}[st.v].\mathtt{len}() - st.pos&amp;lt;/tex&amp;gt;&lt;br /&gt;
          '''return''' &amp;lt;tex&amp;gt;\mathtt{state}(st.v, st.pos + r - l)&amp;lt;/tex&amp;gt;&lt;br /&gt;
        &amp;lt;tex&amp;gt;l = l + \mathtt{t}[st.v].\mathtt{len}() - st.pos&amp;lt;/tex&amp;gt;&lt;br /&gt;
        &amp;lt;tex&amp;gt;st.pos = \mathtt{t}[st.v].\mathtt{len}()&amp;lt;/tex&amp;gt;&lt;br /&gt;
    '''return''' &amp;lt;tex&amp;gt;st&amp;lt;/tex&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  '''function''' &amp;lt;tex&amp;gt;\mathtt{split}(st):&amp;lt;/tex&amp;gt;&lt;br /&gt;
    '''if''' &amp;lt;tex&amp;gt;st.pos == \mathtt{t}[st.v].\mathtt{len}()&amp;lt;/tex&amp;gt;&lt;br /&gt;
      '''return''' &amp;lt;tex&amp;gt;st.v&amp;lt;/tex&amp;gt;&lt;br /&gt;
    '''if''' &amp;lt;tex&amp;gt;st.pos == 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
      '''return''' &amp;lt;tex&amp;gt;\mathtt{t}[st.v].par&amp;lt;/tex&amp;gt;&lt;br /&gt;
    &amp;lt;tex&amp;gt;v = \mathtt{t}[st.v]&amp;lt;/tex&amp;gt;&lt;br /&gt;
    &amp;lt;tex&amp;gt;id = sz&amp;lt;/tex&amp;gt;&lt;br /&gt;
    &amp;lt;tex&amp;gt;sz = sz + 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
    &amp;lt;tex&amp;gt;\mathtt{t}[id] = \mathtt{node}(v.l, v.l + st.pos, v.par)&amp;lt;/tex&amp;gt;&lt;br /&gt;
    &amp;lt;tex&amp;gt;\mathtt{t}[v.par].\mathtt{get}(\mathtt{s}[v.l]) = id&amp;lt;/tex&amp;gt;&lt;br /&gt;
    &amp;lt;tex&amp;gt;\mathtt{t}[id].\mathtt{get}(\mathtt{s}[v.l + st.pos]) = st.v&amp;lt;/tex&amp;gt;&lt;br /&gt;
    &amp;lt;tex&amp;gt;\mathtt{t}[st.v].par = id&amp;lt;/tex&amp;gt;&lt;br /&gt;
    &amp;lt;tex&amp;gt;\mathtt{t}[st.v].l = l + st.pos&amp;lt;/tex&amp;gt;&lt;br /&gt;
    '''return''' &amp;lt;tex&amp;gt;id&amp;lt;/tex&amp;gt;&lt;br /&gt;
  &lt;br /&gt;
  '''function''' &amp;lt;tex&amp;gt;\mathtt{getLink}(v):&amp;lt;/tex&amp;gt;&lt;br /&gt;
    '''if''' &amp;lt;tex&amp;gt;\mathtt{t}[v].link \ne -1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
      '''return''' &amp;lt;tex&amp;gt;\mathtt{t}[v].link&amp;lt;/tex&amp;gt;&lt;br /&gt;
    '''if''' &amp;lt;tex&amp;gt;\mathtt{t}[v].par == -1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
      '''return''' &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt;&lt;br /&gt;
    &amp;lt;tex&amp;gt;to = \mathtt{getLink}(\mathtt{t}[v].par)&amp;lt;/tex&amp;gt;&lt;br /&gt;
    '''return''' &amp;lt;tex&amp;gt;\mathtt{t}[v].link=\mathtt{split}(\mathtt{go}(\mathtt{state}(to,\mathtt{t}[to].\mathtt{len}()),\mathtt{t}[v].l+(\mathtt{t}[v].par==0),\mathtt{t}[v].r))&amp;lt;/tex&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
  '''funciton''' &amp;lt;tex&amp;gt;\mathtt{treeExtend}(pos):&amp;lt;/tex&amp;gt;&lt;br /&gt;
    '''while''' &amp;lt;tex&amp;gt;true&amp;lt;/tex&amp;gt;&lt;br /&gt;
      &amp;lt;tex&amp;gt;nptr = \mathtt{go}(ptr, pos, pos + 1)&amp;lt;/tex&amp;gt;&lt;br /&gt;
      '''if''' &amp;lt;tex&amp;gt;nptr.v \ne -1&amp;lt;/tex&amp;gt;&lt;br /&gt;
        &amp;lt;tex&amp;gt;ptr = nptr&amp;lt;/tex&amp;gt;&lt;br /&gt;
        '''return'''&lt;br /&gt;
      &amp;lt;tex&amp;gt;mid = \mathtt{split}(ptr)&amp;lt;/tex&amp;gt;&lt;br /&gt;
      &amp;lt;tex&amp;gt;leaf = sz&amp;lt;/tex&amp;gt;&lt;br /&gt;
      &amp;lt;tex&amp;gt;sz = sz + 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
      &amp;lt;tex&amp;gt;\mathtt{t}[leaf] = node(pos, n, mid)&amp;lt;/tex&amp;gt;&lt;br /&gt;
      &amp;lt;tex&amp;gt;\mathtt{t}[mid].\mathtt{get}(\mathtt{s}[pos]) = leaf&amp;lt;/tex&amp;gt;&lt;br /&gt;
      &amp;lt;tex&amp;gt;ptr.v = \mathtt{getLink}(mid)&amp;lt;/tex&amp;gt;&lt;br /&gt;
      &amp;lt;tex&amp;gt;ptr.pos = \mathtt{t}[ptr.v].\mathtt{len}()&amp;lt;/tex&amp;gt;&lt;br /&gt;
      '''if''' &amp;lt;tex&amp;gt;!mid&amp;lt;/tex&amp;gt;&lt;br /&gt;
        '''break'''&lt;br /&gt;
 &lt;br /&gt;
  '''function''' &amp;lt;tex&amp;gt;\mathtt{buildTree}():&amp;lt;/tex&amp;gt;&lt;br /&gt;
    &amp;lt;tex&amp;gt;sz = 1&amp;lt;/tex&amp;gt;&lt;br /&gt;
    '''for''' &amp;lt;tex&amp;gt;i = 0...n&amp;lt;/tex&amp;gt;&lt;br /&gt;
      &amp;lt;tex&amp;gt;\mathtt{treeExtend}(i)&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;
* Дэн Гасфилд — Строки, деревья и последовательности в алгоритмах: Информатика и вычислительная биология — СПб.: Невский Диалект; БХВ-Петербург, 2003. — 654 с: ил.&lt;br /&gt;
* [http://e-maxx.ru/algo/ukkonen MAXimal :: algo :: Суффиксное дерево. Алгоритм Укконена]&lt;br /&gt;
* [http://habrahabr.ru/post/111675/ Habrahabr — Построение суффиксного дерева: алгоритм Укконена]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Словарные структуры данных]]&lt;br /&gt;
[[Категория: Суффиксное дерево]]&lt;/div&gt;</summary>
		<author><name>66.249.81.157</name></author>	</entry>

	</feed>