<?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=Nastyastanchul</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=Nastyastanchul"/>
		<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/Nastyastanchul"/>
		<updated>2026-08-04T13:38:29Z</updated>
		<subtitle>Вклад участника</subtitle>
		<generator>MediaWiki 1.30.0</generator>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7_%D1%81%D0%B2%D0%BE%D0%B9%D1%81%D1%82%D0%B2_%D1%80%D0%B5%D0%B3%D1%83%D0%BB%D1%8F%D1%80%D0%BD%D1%8B%D1%85_%D1%8F%D0%B7%D1%8B%D0%BA%D0%BE%D0%B2_(%D0%BF%D1%83%D1%81%D1%82%D0%BE%D1%82%D0%B0,_%D1%81%D0%BE%D0%B2%D0%BF%D0%B0%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5,_%D0%B2%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D0%B5,_%D0%BA%D0%BE%D0%BD%D0%B5%D1%87%D0%BD%D0%BE%D1%81%D1%82%D1%8C,_%D0%BF%D0%BE%D0%B4%D1%81%D1%87%D1%91%D1%82_%D1%87%D0%B8%D1%81%D0%BB%D0%B0_%D1%81%D0%BB%D0%BE%D0%B2)&amp;diff=49689</id>
		<title>Анализ свойств регулярных языков (пустота, совпадение, включение, конечность, подсчёт числа слов)</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7_%D1%81%D0%B2%D0%BE%D0%B9%D1%81%D1%82%D0%B2_%D1%80%D0%B5%D0%B3%D1%83%D0%BB%D1%8F%D1%80%D0%BD%D1%8B%D1%85_%D1%8F%D0%B7%D1%8B%D0%BA%D0%BE%D0%B2_(%D0%BF%D1%83%D1%81%D1%82%D0%BE%D1%82%D0%B0,_%D1%81%D0%BE%D0%B2%D0%BF%D0%B0%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5,_%D0%B2%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D0%B5,_%D0%BA%D0%BE%D0%BD%D0%B5%D1%87%D0%BD%D0%BE%D1%81%D1%82%D1%8C,_%D0%BF%D0%BE%D0%B4%D1%81%D1%87%D1%91%D1%82_%D1%87%D0%B8%D1%81%D0%BB%D0%B0_%D1%81%D0%BB%D0%BE%D0%B2)&amp;diff=49689"/>
				<updated>2015-11-02T18:48:41Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Для различных операций с [[Регулярные языки: два определения и их эквивалентность|регулярными языками]] (англ. ''regular language'') полезно знать некоторые их свойства. Как правило, в доказательствах этих свойств используется факт из теоремы Клини об эквивалентности [[Детерминированные_конечные_автоматы#Автоматные_языки | автоматных]] и [[Регулярные языки:_два определения_и_их_эквивалентность#REG1 | регулярных]]  языков.&lt;br /&gt;
&lt;br /&gt;
== Пустота регулярного языка ==&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=empty&lt;br /&gt;
|definition=&lt;br /&gt;
[[Регулярные языки: два определения и их эквивалентность|Регулярный язык]] называется '''пустым''' (англ ''empty''), если он не содержит ни одного слова.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Язык, содержащий хотя бы одно слово, назовём '''непустым'''.&lt;br /&gt;
&lt;br /&gt;
{{Теорема&lt;br /&gt;
|id=&lt;br /&gt;
regEmpty&lt;br /&gt;
|statement=&lt;br /&gt;
Регулярный язык является непустым тогда и только тогда, когда в любом задающем его автомате существует путь из стартового состояния в какое-либо из терминальных.&lt;br /&gt;
&lt;br /&gt;
|proof=&lt;br /&gt;
&amp;lt;tex&amp;gt;\Rightarrow&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пусть язык содержит слово &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Любой [[Детерминированные конечные автоматы| детерминированный конечный автомат]] &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;, задающий этот язык, должен допускать &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Тогда при переходе из стартового состояния &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; по символам &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt; получится путь, оканчивающийся в одном из терминальных состояний.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\Leftarrow&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;
== Совпадение регулярных языков ==&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=equal&lt;br /&gt;
|definition=&lt;br /&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;
|id=inclusion&lt;br /&gt;
|definition=&lt;br /&gt;
[[Регулярные языки: два определения и их эквивалентность|Регулярный язык]] &amp;lt;tex&amp;gt;L_{1}&amp;lt;/tex&amp;gt; '''входит (включается)''' в регулярный язык &amp;lt;tex&amp;gt;L_{2}&amp;lt;/tex&amp;gt;, если любое слово, принадлежащее &amp;lt;tex&amp;gt;L_{1}&amp;lt;/tex&amp;gt;, принадлежит &amp;lt;tex&amp;gt;L_{2}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Пусть автомат &amp;lt;tex&amp;gt;M_1&amp;lt;/tex&amp;gt; задаёт язык &amp;lt;tex&amp;gt;L_1&amp;lt;/tex&amp;gt;, а автомат &amp;lt;tex&amp;gt;M_2&amp;lt;/tex&amp;gt; — язык &amp;lt;tex&amp;gt;L_1 \cap L_2&amp;lt;/tex&amp;gt;. Для проверки включения &amp;lt;tex&amp;gt;L_1&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;L_2&amp;lt;/tex&amp;gt; достаточно проверить [[Эквивалентность_состояний_ДКА|эквивалентность]] &amp;lt;tex&amp;gt;M_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;M_2&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Конечность регулярного языка, подсчёт числа слов ==&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=finite&lt;br /&gt;
|definition=&lt;br /&gt;
[[Регулярные языки: два определения и их эквивалентность|Регулярный язык]] называется '''конечным''', если принадлежащее ему множество слов конечно.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Теорема&lt;br /&gt;
|id=&lt;br /&gt;
regFinite&lt;br /&gt;
|statement=&lt;br /&gt;
[[Детерминированные конечные автоматы|Детерминированный конечный автомат]] &amp;lt;tex&amp;gt;A_{1}&amp;lt;/tex&amp;gt; задаёт конечный язык тогда и только тогда, когда в &amp;lt;tex&amp;gt;A_{1}&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; достижимо из стартового состояния &amp;lt;tex&amp;gt;s&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;v&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
|proof=&lt;br /&gt;
Пусть такое состояние &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; существует, а строки &amp;lt;tex&amp;gt;x, y, z&amp;lt;/tex&amp;gt; таковы, что &amp;lt;tex&amp;gt;\langle s, xyz \rangle \vdash ^{*} \langle v, yz \rangle \vdash ^{*} \langle v, z \rangle \vdash ^{*} \langle t, \epsilon \rangle&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; — допускающее, &amp;lt;tex&amp;gt;y&amp;lt;/tex&amp;gt; — непустая. Рассмотрим строки вида &amp;lt;tex&amp;gt;xy^{k}z, k \in \mathbb{N}&amp;lt;/tex&amp;gt;. Их бесконечное количество, и все они, как легко увидеть, допускаются автоматом. Значит, язык бесконечен.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Пусть такого состояния не существует. Тогда любой путь из стартового состояния в какое-либо из допускающих является простым. Количество слов в языке равно количеству таких путей; количество путей, в свою очередь, ограничено &amp;lt;tex&amp;gt;n! | \Sigma |^{n-1}&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; — количество состояний автомата: &amp;lt;tex&amp;gt;n!&amp;lt;/tex&amp;gt; — количество перестановок состояний, &amp;lt;tex&amp;gt;| \Sigma |^{n-1}&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;\mathrm{paths} (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;; &amp;lt;tex&amp;gt;\mathrm{paths} (s) = 1&amp;lt;/tex&amp;gt;. Заметим, что если известны значения &amp;lt;tex&amp;gt;\mathrm{paths} (u)&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;\mathrm{paths} (v) = \sum\limits_{u}\mathrm{paths} (u)&amp;lt;/tex&amp;gt;. Количеством слов в языке будет сумма &amp;lt;tex&amp;gt;\mathrm{paths} (t)&amp;lt;/tex&amp;gt; для всех допускающих &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Источики информации ==&lt;br /&gt;
&lt;br /&gt;
* ''Хопкрофт Д., Мотвани Р., Ульман Д.'' Введение в теорию автоматов, языков и вычислений, 2-е изд. / Пер. с англ. — Москва: Издательский дом «Вильямс», 2002. — с. 169-177: ISBN 5-8459-0261-4 (рус.)&lt;br /&gt;
&lt;br /&gt;
[[Категория: Теория формальных языков]]&lt;br /&gt;
[[Категория: Автоматы и регулярные языки]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7_%D1%81%D0%B2%D0%BE%D0%B9%D1%81%D1%82%D0%B2_%D1%80%D0%B5%D0%B3%D1%83%D0%BB%D1%8F%D1%80%D0%BD%D1%8B%D1%85_%D1%8F%D0%B7%D1%8B%D0%BA%D0%BE%D0%B2_(%D0%BF%D1%83%D1%81%D1%82%D0%BE%D1%82%D0%B0,_%D1%81%D0%BE%D0%B2%D0%BF%D0%B0%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5,_%D0%B2%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D0%B5,_%D0%BA%D0%BE%D0%BD%D0%B5%D1%87%D0%BD%D0%BE%D1%81%D1%82%D1%8C,_%D0%BF%D0%BE%D0%B4%D1%81%D1%87%D1%91%D1%82_%D1%87%D0%B8%D1%81%D0%BB%D0%B0_%D1%81%D0%BB%D0%BE%D0%B2)&amp;diff=49637</id>
		<title>Анализ свойств регулярных языков (пустота, совпадение, включение, конечность, подсчёт числа слов)</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7_%D1%81%D0%B2%D0%BE%D0%B9%D1%81%D1%82%D0%B2_%D1%80%D0%B5%D0%B3%D1%83%D0%BB%D1%8F%D1%80%D0%BD%D1%8B%D1%85_%D1%8F%D0%B7%D1%8B%D0%BA%D0%BE%D0%B2_(%D0%BF%D1%83%D1%81%D1%82%D0%BE%D1%82%D0%B0,_%D1%81%D0%BE%D0%B2%D0%BF%D0%B0%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5,_%D0%B2%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D0%B5,_%D0%BA%D0%BE%D0%BD%D0%B5%D1%87%D0%BD%D0%BE%D1%81%D1%82%D1%8C,_%D0%BF%D0%BE%D0%B4%D1%81%D1%87%D1%91%D1%82_%D1%87%D0%B8%D1%81%D0%BB%D0%B0_%D1%81%D0%BB%D0%BE%D0%B2)&amp;diff=49637"/>
				<updated>2015-10-13T14:25:10Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Для различных операций с [[Регулярные языки: два определения и их эквивалентность|регулярными языками]] (англ. ''regular language'') полезно знать некоторые их свойства. Как правило, в доказательствах этих свойств используется факт из теоремы Клини о эквивалентности [[Детерминированные_конечные_автоматы#Автоматные_языки | автоматных]] и [[Регулярные языки:_два определения_и_их_эквивалентность#REG1 | регулярных]]  языков.&lt;br /&gt;
&lt;br /&gt;
== Пустота регулярного языка ==&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=empty&lt;br /&gt;
|definition=&lt;br /&gt;
[[Регулярные языки: два определения и их эквивалентность|Регулярный язык]] называется '''пустым''' (англ ''empty''), если он не содержит ни одного слова.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Язык, содержащий хотя бы одно слово, назовём '''непустым'''.&lt;br /&gt;
&lt;br /&gt;
{{Теорема&lt;br /&gt;
|id=&lt;br /&gt;
regEmpty&lt;br /&gt;
|statement=&lt;br /&gt;
Регулярный язык является непустым тогда и только тогда, когда в любом задающем его автомате существует путь из стартового состояния в какое-либо из терминальных.&lt;br /&gt;
&lt;br /&gt;
|proof=&lt;br /&gt;
&amp;lt;tex&amp;gt;\Rightarrow&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пусть язык содержит слово &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Любой [[Детерминированные конечные автоматы| детерминированный конечный автомат]] &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;, задающий этот язык, должен допускать &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Тогда при переходе из стартового состояния &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; по символам &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt; получится путь, оканчивающийся в одном из терминальных состояний.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\Leftarrow&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;
== Совпадение регулярных языков ==&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=equal&lt;br /&gt;
|definition=&lt;br /&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;
|id=inclusion&lt;br /&gt;
|definition=&lt;br /&gt;
[[Регулярные языки: два определения и их эквивалентность|Регулярный язык]] &amp;lt;tex&amp;gt;L_{1}&amp;lt;/tex&amp;gt; '''входит (включается)''' в регулярный язык &amp;lt;tex&amp;gt;L_{2}&amp;lt;/tex&amp;gt;, если любое слово, принадлежащее &amp;lt;tex&amp;gt;L_{1}&amp;lt;/tex&amp;gt;, принадлежит &amp;lt;tex&amp;gt;L_{2}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Пусть автомат &amp;lt;tex&amp;gt;M_1&amp;lt;/tex&amp;gt; задаёт язык &amp;lt;tex&amp;gt;L_1&amp;lt;/tex&amp;gt;, а автомат &amp;lt;tex&amp;gt;M_2&amp;lt;/tex&amp;gt; — язык &amp;lt;tex&amp;gt;L_1 \cap L_2&amp;lt;/tex&amp;gt;. Для проверки включения &amp;lt;tex&amp;gt;L_1&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;L_2&amp;lt;/tex&amp;gt; достаточно проверить [[Эквивалентность_состояний_ДКА|эквивалентность]] &amp;lt;tex&amp;gt;M_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;M_2&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Конечность регулярного языка, подсчёт числа слов ==&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=finite&lt;br /&gt;
|definition=&lt;br /&gt;
[[Регулярные языки: два определения и их эквивалентность|Регулярный язык]] называется '''конечным''', если принадлежащее ему множество слов конечно.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Теорема&lt;br /&gt;
|id=&lt;br /&gt;
regFinite&lt;br /&gt;
|statement=&lt;br /&gt;
[[Детерминированные конечные автоматы|Детерминированный конечный автомат]] &amp;lt;tex&amp;gt;A_{1}&amp;lt;/tex&amp;gt; задаёт конечный язык тогда и только тогда, когда в &amp;lt;tex&amp;gt;A_{1}&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; достижимо из стартового состояния &amp;lt;tex&amp;gt;s&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;v&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
|proof=&lt;br /&gt;
Пусть такое состояние &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; существует, а строки &amp;lt;tex&amp;gt;x, y, z&amp;lt;/tex&amp;gt; таковы, что &amp;lt;tex&amp;gt;\langle s, xyz \rangle \vdash ^{*} \langle v, yz \rangle \vdash ^{*} \langle v, z \rangle \vdash ^{*} \langle t, \epsilon \rangle&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; — допускающее, &amp;lt;tex&amp;gt;y&amp;lt;/tex&amp;gt; — непустая. Рассмотрим строки вида &amp;lt;tex&amp;gt;xy^{k}z, k \in \mathbb{N}&amp;lt;/tex&amp;gt;. Их бесконечное количество, и все они, как легко увидеть, допускаются автоматом. Значит, язык бесконечен.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Пусть такого состояния не существует. Тогда любой путь из стартового состояния в какое-либо из допускающих является простым. Количество слов в языке равно количеству таких путей; количество путей, в свою очередь, ограничено &amp;lt;tex&amp;gt;n! | \Sigma |^{n-1}&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; — количество состояний автомата: &amp;lt;tex&amp;gt;n!&amp;lt;/tex&amp;gt; — количество перестановок состояний, &amp;lt;tex&amp;gt;| \Sigma |^{n-1}&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;\mathrm{paths} (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;; &amp;lt;tex&amp;gt;\mathrm{paths} (s) = 1&amp;lt;/tex&amp;gt;. Заметим, что если известны значения &amp;lt;tex&amp;gt;\mathrm{paths} (u)&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;\mathrm{paths} (v) = \sum\limits_{u}\mathrm{paths} (u)&amp;lt;/tex&amp;gt;. Количеством слов в языке будет сумма &amp;lt;tex&amp;gt;\mathrm{paths} (t)&amp;lt;/tex&amp;gt; для всех допускающих &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Источики информации ==&lt;br /&gt;
&lt;br /&gt;
* ''Хопкрофт Д., Мотвани Р., Ульман Д.'' Введение в теорию автоматов, языков и вычислений, 2-е изд. / Пер. с англ. — Москва: Издательский дом «Вильямс», 2002. — с. 169-177: ISBN 5-8459-0261-4 (рус.)&lt;br /&gt;
&lt;br /&gt;
[[Категория: Теория формальных языков]]&lt;br /&gt;
[[Категория: Автоматы и регулярные языки]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7_%D1%81%D0%B2%D0%BE%D0%B9%D1%81%D1%82%D0%B2_%D1%80%D0%B5%D0%B3%D1%83%D0%BB%D1%8F%D1%80%D0%BD%D1%8B%D1%85_%D1%8F%D0%B7%D1%8B%D0%BA%D0%BE%D0%B2_(%D0%BF%D1%83%D1%81%D1%82%D0%BE%D1%82%D0%B0,_%D1%81%D0%BE%D0%B2%D0%BF%D0%B0%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5,_%D0%B2%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D0%B5,_%D0%BA%D0%BE%D0%BD%D0%B5%D1%87%D0%BD%D0%BE%D1%81%D1%82%D1%8C,_%D0%BF%D0%BE%D0%B4%D1%81%D1%87%D1%91%D1%82_%D1%87%D0%B8%D1%81%D0%BB%D0%B0_%D1%81%D0%BB%D0%BE%D0%B2)&amp;diff=49636</id>
		<title>Анализ свойств регулярных языков (пустота, совпадение, включение, конечность, подсчёт числа слов)</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7_%D1%81%D0%B2%D0%BE%D0%B9%D1%81%D1%82%D0%B2_%D1%80%D0%B5%D0%B3%D1%83%D0%BB%D1%8F%D1%80%D0%BD%D1%8B%D1%85_%D1%8F%D0%B7%D1%8B%D0%BA%D0%BE%D0%B2_(%D0%BF%D1%83%D1%81%D1%82%D0%BE%D1%82%D0%B0,_%D1%81%D0%BE%D0%B2%D0%BF%D0%B0%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5,_%D0%B2%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D0%B5,_%D0%BA%D0%BE%D0%BD%D0%B5%D1%87%D0%BD%D0%BE%D1%81%D1%82%D1%8C,_%D0%BF%D0%BE%D0%B4%D1%81%D1%87%D1%91%D1%82_%D1%87%D0%B8%D1%81%D0%BB%D0%B0_%D1%81%D0%BB%D0%BE%D0%B2)&amp;diff=49636"/>
				<updated>2015-10-13T14:23:55Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Для различных операций с [[Регулярные языки: два определения и их эквивалентность|регулярными языками]] (англ. ''regular language'') полезно знать некоторые их свойства. Как правило, в доказательствах этих свойств используется факт эквивалентности [[Детерминированные_конечные_автоматы#Автоматные_языки | автоматных]] и [[Регулярные языки:_два определения_и_их_эквивалентность#REG1 | регулярных]]  языков.&lt;br /&gt;
&lt;br /&gt;
== Пустота регулярного языка ==&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=empty&lt;br /&gt;
|definition=&lt;br /&gt;
[[Регулярные языки: два определения и их эквивалентность|Регулярный язык]] называется '''пустым''' (англ ''empty''), если он не содержит ни одного слова.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Язык, содержащий хотя бы одно слово, назовём '''непустым'''.&lt;br /&gt;
&lt;br /&gt;
{{Теорема&lt;br /&gt;
|id=&lt;br /&gt;
regEmpty&lt;br /&gt;
|statement=&lt;br /&gt;
Регулярный язык является непустым тогда и только тогда, когда в любом задающем его автомате существует путь из стартового состояния в какое-либо из терминальных.&lt;br /&gt;
&lt;br /&gt;
|proof=&lt;br /&gt;
&amp;lt;tex&amp;gt;\Rightarrow&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пусть язык содержит слово &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Любой [[Детерминированные конечные автоматы| детерминированный конечный автомат]] &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;, задающий этот язык, должен допускать &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Тогда при переходе из стартового состояния &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; по символам &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt; получится путь, оканчивающийся в одном из терминальных состояний.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\Leftarrow&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;
== Совпадение регулярных языков ==&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=equal&lt;br /&gt;
|definition=&lt;br /&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;
|id=inclusion&lt;br /&gt;
|definition=&lt;br /&gt;
[[Регулярные языки: два определения и их эквивалентность|Регулярный язык]] &amp;lt;tex&amp;gt;L_{1}&amp;lt;/tex&amp;gt; '''входит (включается)''' в регулярный язык &amp;lt;tex&amp;gt;L_{2}&amp;lt;/tex&amp;gt;, если любое слово, принадлежащее &amp;lt;tex&amp;gt;L_{1}&amp;lt;/tex&amp;gt;, принадлежит &amp;lt;tex&amp;gt;L_{2}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Пусть автомат &amp;lt;tex&amp;gt;M_1&amp;lt;/tex&amp;gt; задаёт язык &amp;lt;tex&amp;gt;L_1&amp;lt;/tex&amp;gt;, а автомат &amp;lt;tex&amp;gt;M_2&amp;lt;/tex&amp;gt; — язык &amp;lt;tex&amp;gt;L_1 \cap L_2&amp;lt;/tex&amp;gt;. Для проверки включения &amp;lt;tex&amp;gt;L_1&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;L_2&amp;lt;/tex&amp;gt; достаточно проверить [[Эквивалентность_состояний_ДКА|эквивалентность]] &amp;lt;tex&amp;gt;M_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;M_2&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Конечность регулярного языка, подсчёт числа слов ==&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=finite&lt;br /&gt;
|definition=&lt;br /&gt;
[[Регулярные языки: два определения и их эквивалентность|Регулярный язык]] называется '''конечным''', если принадлежащее ему множество слов конечно.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Теорема&lt;br /&gt;
|id=&lt;br /&gt;
regFinite&lt;br /&gt;
|statement=&lt;br /&gt;
[[Детерминированные конечные автоматы|Детерминированный конечный автомат]] &amp;lt;tex&amp;gt;A_{1}&amp;lt;/tex&amp;gt; задаёт конечный язык тогда и только тогда, когда в &amp;lt;tex&amp;gt;A_{1}&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; достижимо из стартового состояния &amp;lt;tex&amp;gt;s&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;v&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
|proof=&lt;br /&gt;
Пусть такое состояние &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; существует, а строки &amp;lt;tex&amp;gt;x, y, z&amp;lt;/tex&amp;gt; таковы, что &amp;lt;tex&amp;gt;\langle s, xyz \rangle \vdash ^{*} \langle v, yz \rangle \vdash ^{*} \langle v, z \rangle \vdash ^{*} \langle t, \epsilon \rangle&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; — допускающее, &amp;lt;tex&amp;gt;y&amp;lt;/tex&amp;gt; — непустая. Рассмотрим строки вида &amp;lt;tex&amp;gt;xy^{k}z, k \in \mathbb{N}&amp;lt;/tex&amp;gt;. Их бесконечное количество, и все они, как легко увидеть, допускаются автоматом. Значит, язык бесконечен.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Пусть такого состояния не существует. Тогда любой путь из стартового состояния в какое-либо из допускающих является простым. Количество слов в языке равно количеству таких путей; количество путей, в свою очередь, ограничено &amp;lt;tex&amp;gt;n! | \Sigma |^{n-1}&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; — количество состояний автомата: &amp;lt;tex&amp;gt;n!&amp;lt;/tex&amp;gt; — количество перестановок состояний, &amp;lt;tex&amp;gt;| \Sigma |^{n-1}&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;\mathrm{paths} (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;; &amp;lt;tex&amp;gt;\mathrm{paths} (s) = 1&amp;lt;/tex&amp;gt;. Заметим, что если известны значения &amp;lt;tex&amp;gt;\mathrm{paths} (u)&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;\mathrm{paths} (v) = \sum\limits_{u}\mathrm{paths} (u)&amp;lt;/tex&amp;gt;. Количеством слов в языке будет сумма &amp;lt;tex&amp;gt;\mathrm{paths} (t)&amp;lt;/tex&amp;gt; для всех допускающих &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Источики информации ==&lt;br /&gt;
&lt;br /&gt;
* ''Хопкрофт Д., Мотвани Р., Ульман Д.'' Введение в теорию автоматов, языков и вычислений, 2-е изд. / Пер. с англ. — Москва: Издательский дом «Вильямс», 2002. — с. 169-177: ISBN 5-8459-0261-4 (рус.)&lt;br /&gt;
&lt;br /&gt;
[[Категория: Теория формальных языков]]&lt;br /&gt;
[[Категория: Автоматы и регулярные языки]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7_%D1%81%D0%B2%D0%BE%D0%B9%D1%81%D1%82%D0%B2_%D1%80%D0%B5%D0%B3%D1%83%D0%BB%D1%8F%D1%80%D0%BD%D1%8B%D1%85_%D1%8F%D0%B7%D1%8B%D0%BA%D0%BE%D0%B2_(%D0%BF%D1%83%D1%81%D1%82%D0%BE%D1%82%D0%B0,_%D1%81%D0%BE%D0%B2%D0%BF%D0%B0%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5,_%D0%B2%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D0%B5,_%D0%BA%D0%BE%D0%BD%D0%B5%D1%87%D0%BD%D0%BE%D1%81%D1%82%D1%8C,_%D0%BF%D0%BE%D0%B4%D1%81%D1%87%D1%91%D1%82_%D1%87%D0%B8%D1%81%D0%BB%D0%B0_%D1%81%D0%BB%D0%BE%D0%B2)&amp;diff=49634</id>
		<title>Анализ свойств регулярных языков (пустота, совпадение, включение, конечность, подсчёт числа слов)</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7_%D1%81%D0%B2%D0%BE%D0%B9%D1%81%D1%82%D0%B2_%D1%80%D0%B5%D0%B3%D1%83%D0%BB%D1%8F%D1%80%D0%BD%D1%8B%D1%85_%D1%8F%D0%B7%D1%8B%D0%BA%D0%BE%D0%B2_(%D0%BF%D1%83%D1%81%D1%82%D0%BE%D1%82%D0%B0,_%D1%81%D0%BE%D0%B2%D0%BF%D0%B0%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5,_%D0%B2%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D0%B5,_%D0%BA%D0%BE%D0%BD%D0%B5%D1%87%D0%BD%D0%BE%D1%81%D1%82%D1%8C,_%D0%BF%D0%BE%D0%B4%D1%81%D1%87%D1%91%D1%82_%D1%87%D0%B8%D1%81%D0%BB%D0%B0_%D1%81%D0%BB%D0%BE%D0%B2)&amp;diff=49634"/>
				<updated>2015-10-13T07:05:46Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Литература */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Для различных операций с [[Регулярные языки: два определения и их эквивалентность|регулярными языками]] полезно знать некоторые их свойства. Как правило, в доказательствах этих свойств используется факт эквивалентности автоматных и регулярных языков.&lt;br /&gt;
&lt;br /&gt;
== Пустота регулярного языка ==&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=empty&lt;br /&gt;
|definition=&lt;br /&gt;
[[Регулярные языки: два определения и их эквивалентность|Регулярный язык]] называется '''пустым''', если он не содержит ни одного слова.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Язык, содержащий хотя бы одно слово, назовём '''непустым'''.&lt;br /&gt;
&lt;br /&gt;
{{Теорема&lt;br /&gt;
|id=&lt;br /&gt;
regEmpty&lt;br /&gt;
|statement=&lt;br /&gt;
Регулярный язык является непустым тогда и только тогда, когда в любом задающем его автомате существует путь из стартового состояния в какое-либо из терминальных.&lt;br /&gt;
&lt;br /&gt;
|proof=&lt;br /&gt;
&amp;lt;tex&amp;gt;\Rightarrow&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пусть язык содержит слово &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Любой [[Детерминированные конечные автоматы| детерминированный конечный автомат]] &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;, задающий этот язык, должен допускать &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Тогда при переходе из стартового состояния &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; по символам &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt; получится путь, оканчивающийся в одном из терминальных состояний.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\Leftarrow&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;
== Совпадение регулярных языков ==&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=equal&lt;br /&gt;
|definition=&lt;br /&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;
|id=inclusion&lt;br /&gt;
|definition=&lt;br /&gt;
[[Регулярные языки: два определения и их эквивалентность|Регулярный язык]] &amp;lt;tex&amp;gt;L_{1}&amp;lt;/tex&amp;gt; '''входит (включается)''' в регулярный язык &amp;lt;tex&amp;gt;L_{2}&amp;lt;/tex&amp;gt;, если любое слово, принадлежащее &amp;lt;tex&amp;gt;L_{1}&amp;lt;/tex&amp;gt;, принадлежит &amp;lt;tex&amp;gt;L_{2}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Пусть автомат &amp;lt;tex&amp;gt;M_1&amp;lt;/tex&amp;gt; задаёт язык &amp;lt;tex&amp;gt;L_1&amp;lt;/tex&amp;gt;, а автомат &amp;lt;tex&amp;gt;M_2&amp;lt;/tex&amp;gt; — язык &amp;lt;tex&amp;gt;L_1 \cap L_2&amp;lt;/tex&amp;gt;. Для проверки включения &amp;lt;tex&amp;gt;L_1&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;L_2&amp;lt;/tex&amp;gt; достаточно проверить [[Эквивалентность_состояний_ДКА|эквивалентность]] &amp;lt;tex&amp;gt;M_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;M_2&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Конечность регулярного языка, подсчёт числа слов ==&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=finite&lt;br /&gt;
|definition=&lt;br /&gt;
[[Регулярные языки: два определения и их эквивалентность|Регулярный язык]] называется '''конечным''', если принадлежащее ему множество слов конечно.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Теорема&lt;br /&gt;
|id=&lt;br /&gt;
regFinite&lt;br /&gt;
|statement=&lt;br /&gt;
[[Детерминированные конечные автоматы|Детерминированный конечный автомат]] &amp;lt;tex&amp;gt;A_{1}&amp;lt;/tex&amp;gt; задаёт конечный язык тогда и только тогда, когда в &amp;lt;tex&amp;gt;A_{1}&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; достижимо из стартового состояния &amp;lt;tex&amp;gt;s&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;v&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
|proof=&lt;br /&gt;
Пусть такое состояние &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; существует, а строки &amp;lt;tex&amp;gt;x, y, z&amp;lt;/tex&amp;gt; таковы, что &amp;lt;tex&amp;gt;\langle s, xyz \rangle \vdash ^{*} \langle v, yz \rangle \vdash ^{*} \langle v, z \rangle \vdash ^{*} \langle t, \epsilon \rangle&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; — допускающее, &amp;lt;tex&amp;gt;y&amp;lt;/tex&amp;gt; — непустая. Рассмотрим строки вида &amp;lt;tex&amp;gt;xy^{k}z, k \in \mathbb{N}&amp;lt;/tex&amp;gt;. Их бесконечное количество, и все они, как легко увидеть, допускаются автоматом. Значит, язык бесконечен.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Пусть такого состояния не существует. Тогда любой путь из стартового состояния в какое-либо из допускающих является простым. Количество слов в языке равно количеству таких путей; количество путей, в свою очередь, ограничено &amp;lt;tex&amp;gt;n! | \Sigma |^{n-1}&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; — количество состояний автомата: &amp;lt;tex&amp;gt;n!&amp;lt;/tex&amp;gt; — количество перестановок состояний, &amp;lt;tex&amp;gt;| \Sigma |^{n-1}&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;\mathrm{paths} (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;; &amp;lt;tex&amp;gt;\mathrm{paths} (s) = 1&amp;lt;/tex&amp;gt;. Заметим, что если известны значения &amp;lt;tex&amp;gt;\mathrm{paths} (u)&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;\mathrm{paths} (v) = \sum\limits_{u}\mathrm{paths} (u)&amp;lt;/tex&amp;gt;. Количеством слов в языке будет сумма &amp;lt;tex&amp;gt;\mathrm{paths} (t)&amp;lt;/tex&amp;gt; для всех допускающих &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Источики информации ==&lt;br /&gt;
&lt;br /&gt;
* ''Хопкрофт Д., Мотвани Р., Ульман Д.'' Введение в теорию автоматов, языков и вычислений, 2-е изд. / Пер. с англ. — Москва: Издательский дом «Вильямс», 2002. — с. 169-177: ISBN 5-8459-0261-4 (рус.)&lt;br /&gt;
&lt;br /&gt;
[[Категория: Теория формальных языков]]&lt;br /&gt;
[[Категория: Автоматы и регулярные языки]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7_%D1%81%D0%B2%D0%BE%D0%B9%D1%81%D1%82%D0%B2_%D1%80%D0%B5%D0%B3%D1%83%D0%BB%D1%8F%D1%80%D0%BD%D1%8B%D1%85_%D1%8F%D0%B7%D1%8B%D0%BA%D0%BE%D0%B2_(%D0%BF%D1%83%D1%81%D1%82%D0%BE%D1%82%D0%B0,_%D1%81%D0%BE%D0%B2%D0%BF%D0%B0%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5,_%D0%B2%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D0%B5,_%D0%BA%D0%BE%D0%BD%D0%B5%D1%87%D0%BD%D0%BE%D1%81%D1%82%D1%8C,_%D0%BF%D0%BE%D0%B4%D1%81%D1%87%D1%91%D1%82_%D1%87%D0%B8%D1%81%D0%BB%D0%B0_%D1%81%D0%BB%D0%BE%D0%B2)&amp;diff=49633</id>
		<title>Анализ свойств регулярных языков (пустота, совпадение, включение, конечность, подсчёт числа слов)</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7_%D1%81%D0%B2%D0%BE%D0%B9%D1%81%D1%82%D0%B2_%D1%80%D0%B5%D0%B3%D1%83%D0%BB%D1%8F%D1%80%D0%BD%D1%8B%D1%85_%D1%8F%D0%B7%D1%8B%D0%BA%D0%BE%D0%B2_(%D0%BF%D1%83%D1%81%D1%82%D0%BE%D1%82%D0%B0,_%D1%81%D0%BE%D0%B2%D0%BF%D0%B0%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5,_%D0%B2%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D0%B5,_%D0%BA%D0%BE%D0%BD%D0%B5%D1%87%D0%BD%D0%BE%D1%81%D1%82%D1%8C,_%D0%BF%D0%BE%D0%B4%D1%81%D1%87%D1%91%D1%82_%D1%87%D0%B8%D1%81%D0%BB%D0%B0_%D1%81%D0%BB%D0%BE%D0%B2)&amp;diff=49633"/>
				<updated>2015-10-13T07:03:01Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Для различных операций с [[Регулярные языки: два определения и их эквивалентность|регулярными языками]] полезно знать некоторые их свойства. Как правило, в доказательствах этих свойств используется факт эквивалентности автоматных и регулярных языков.&lt;br /&gt;
&lt;br /&gt;
== Пустота регулярного языка ==&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=empty&lt;br /&gt;
|definition=&lt;br /&gt;
[[Регулярные языки: два определения и их эквивалентность|Регулярный язык]] называется '''пустым''', если он не содержит ни одного слова.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Язык, содержащий хотя бы одно слово, назовём '''непустым'''.&lt;br /&gt;
&lt;br /&gt;
{{Теорема&lt;br /&gt;
|id=&lt;br /&gt;
regEmpty&lt;br /&gt;
|statement=&lt;br /&gt;
Регулярный язык является непустым тогда и только тогда, когда в любом задающем его автомате существует путь из стартового состояния в какое-либо из терминальных.&lt;br /&gt;
&lt;br /&gt;
|proof=&lt;br /&gt;
&amp;lt;tex&amp;gt;\Rightarrow&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Пусть язык содержит слово &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Любой [[Детерминированные конечные автоматы| детерминированный конечный автомат]] &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;, задающий этот язык, должен допускать &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Тогда при переходе из стартового состояния &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt; по символам &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt; получится путь, оканчивающийся в одном из терминальных состояний.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tex&amp;gt;\Leftarrow&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;
== Совпадение регулярных языков ==&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=equal&lt;br /&gt;
|definition=&lt;br /&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;
|id=inclusion&lt;br /&gt;
|definition=&lt;br /&gt;
[[Регулярные языки: два определения и их эквивалентность|Регулярный язык]] &amp;lt;tex&amp;gt;L_{1}&amp;lt;/tex&amp;gt; '''входит (включается)''' в регулярный язык &amp;lt;tex&amp;gt;L_{2}&amp;lt;/tex&amp;gt;, если любое слово, принадлежащее &amp;lt;tex&amp;gt;L_{1}&amp;lt;/tex&amp;gt;, принадлежит &amp;lt;tex&amp;gt;L_{2}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Пусть автомат &amp;lt;tex&amp;gt;M_1&amp;lt;/tex&amp;gt; задаёт язык &amp;lt;tex&amp;gt;L_1&amp;lt;/tex&amp;gt;, а автомат &amp;lt;tex&amp;gt;M_2&amp;lt;/tex&amp;gt; — язык &amp;lt;tex&amp;gt;L_1 \cap L_2&amp;lt;/tex&amp;gt;. Для проверки включения &amp;lt;tex&amp;gt;L_1&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;L_2&amp;lt;/tex&amp;gt; достаточно проверить [[Эквивалентность_состояний_ДКА|эквивалентность]] &amp;lt;tex&amp;gt;M_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;M_2&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Конечность регулярного языка, подсчёт числа слов ==&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|id=finite&lt;br /&gt;
|definition=&lt;br /&gt;
[[Регулярные языки: два определения и их эквивалентность|Регулярный язык]] называется '''конечным''', если принадлежащее ему множество слов конечно.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Теорема&lt;br /&gt;
|id=&lt;br /&gt;
regFinite&lt;br /&gt;
|statement=&lt;br /&gt;
[[Детерминированные конечные автоматы|Детерминированный конечный автомат]] &amp;lt;tex&amp;gt;A_{1}&amp;lt;/tex&amp;gt; задаёт конечный язык тогда и только тогда, когда в &amp;lt;tex&amp;gt;A_{1}&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; достижимо из стартового состояния &amp;lt;tex&amp;gt;s&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;v&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
|proof=&lt;br /&gt;
Пусть такое состояние &amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt; существует, а строки &amp;lt;tex&amp;gt;x, y, z&amp;lt;/tex&amp;gt; таковы, что &amp;lt;tex&amp;gt;\langle s, xyz \rangle \vdash ^{*} \langle v, yz \rangle \vdash ^{*} \langle v, z \rangle \vdash ^{*} \langle t, \epsilon \rangle&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; — допускающее, &amp;lt;tex&amp;gt;y&amp;lt;/tex&amp;gt; — непустая. Рассмотрим строки вида &amp;lt;tex&amp;gt;xy^{k}z, k \in \mathbb{N}&amp;lt;/tex&amp;gt;. Их бесконечное количество, и все они, как легко увидеть, допускаются автоматом. Значит, язык бесконечен.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Пусть такого состояния не существует. Тогда любой путь из стартового состояния в какое-либо из допускающих является простым. Количество слов в языке равно количеству таких путей; количество путей, в свою очередь, ограничено &amp;lt;tex&amp;gt;n! | \Sigma |^{n-1}&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; — количество состояний автомата: &amp;lt;tex&amp;gt;n!&amp;lt;/tex&amp;gt; — количество перестановок состояний, &amp;lt;tex&amp;gt;| \Sigma |^{n-1}&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;\mathrm{paths} (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;; &amp;lt;tex&amp;gt;\mathrm{paths} (s) = 1&amp;lt;/tex&amp;gt;. Заметим, что если известны значения &amp;lt;tex&amp;gt;\mathrm{paths} (u)&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;\mathrm{paths} (v) = \sum\limits_{u}\mathrm{paths} (u)&amp;lt;/tex&amp;gt;. Количеством слов в языке будет сумма &amp;lt;tex&amp;gt;\mathrm{paths} (t)&amp;lt;/tex&amp;gt; для всех допускающих &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Литература ==&lt;br /&gt;
&lt;br /&gt;
* ''Хопкрофт Д., Мотвани Р., Ульман Д.'' Введение в теорию автоматов, языков и вычислений, 2-е изд. / Пер. с англ. — Москва: Издательский дом «Вильямс», 2002. — с. 169-177: ISBN 5-8459-0261-4 (рус.)&lt;br /&gt;
&lt;br /&gt;
[[Категория: Теория формальных языков]]&lt;br /&gt;
[[Категория: Автоматы и регулярные языки]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8_RMQ_%D0%BA_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B5_LCA&amp;diff=48224</id>
		<title>Сведение задачи RMQ к задаче LCA</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8_RMQ_%D0%BA_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B5_LCA&amp;diff=48224"/>
				<updated>2015-06-09T10:14:17Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Описание */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Шаблон:Задача&lt;br /&gt;
|definition =Дан массив &amp;lt;tex&amp;gt;A[1..N]&amp;lt;/tex&amp;gt;. Поступают запросы вида &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, на каждый запрос требуется найти минимум в массиве &amp;lt;tex&amp;gt;A&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;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Алгоритм ==&lt;br /&gt;
===Описание===&lt;br /&gt;
[[Файл:Wiki.PNG|thumb|right|400x200px|Пример декартового дерева]]&lt;br /&gt;
Будем решать задачу RMQ, уже умея решать задачу LCA. Для хранения решения задачи о LCA будем использовать  [[декартово дерево по неявному ключу]]. Тогда минимум на отрезке от &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; A &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; A &amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Декартово дерево по неявному ключу на массиве &amp;lt;tex&amp;gt;A[1..N]&amp;lt;/tex&amp;gt; {{---}} это бинарное дерево, допускающее следующее рекурсивное построение:&lt;br /&gt;
* Корнем дерева является элемент массива, имеющий минимальное значение &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;, скажем &amp;lt;tex&amp;gt;A[i]&amp;lt;/tex&amp;gt;. Если минимальных элементов несколько, можно взять любой.&lt;br /&gt;
* Левым поддеревом является декартово дерево на массиве &amp;lt;tex&amp;gt;A[1..i-1]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
* Правым поддеревом является декартово дерево на массиве &amp;lt;tex&amp;gt;A[i+1..N]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Здесь и далее &amp;lt;tex&amp;gt;A[i]&amp;lt;/tex&amp;gt; будет также использоваться для обозначения соответствующей вершины дерева.&lt;br /&gt;
&lt;br /&gt;
Построим декартово дерево на массиве &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;. Тогда &amp;lt;tex&amp;gt;RMQ(i, j)&amp;lt;/tex&amp;gt; = &amp;lt;tex&amp;gt;LCA(A[i], A[j])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&amp;lt;br clear=&amp;quot;all&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Корректность ===&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement=&lt;br /&gt;
&amp;lt;tex&amp;gt;RMQ(i, j)&amp;lt;/tex&amp;gt; = &amp;lt;tex&amp;gt;LCA(A[i], A[j])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
Положим &amp;lt;tex&amp;gt;w = LCA(A[i], A[j])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что &amp;lt;tex&amp;gt;A[i]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;A[j]&amp;lt;/tex&amp;gt; не принадлежат одновременно либо правому, либо левому поддереву &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;, потому как тогда бы соответствующий сын находился на большей глубине, чем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;, и также являлся предком как &amp;lt;tex&amp;gt;A[i]&amp;lt;/tex&amp;gt; так и &amp;lt;tex&amp;gt;A[j]&amp;lt;/tex&amp;gt;, что противоречит определению &amp;lt;tex&amp;gt;LCA&amp;lt;/tex&amp;gt;. Из этого замечанию следует, что &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt; лежит между &amp;lt;tex&amp;gt;A[i]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;A[j]&amp;lt;/tex&amp;gt; и, следовательно, принадлежит отрезку &amp;lt;tex&amp;gt;A[i..j]&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;w&amp;lt;/tex&amp;gt; содержит в себе подмассив &amp;lt;tex&amp;gt;A[i..j]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Суммируя, получаем, что &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt; имеет минимальное значение на отрезке, покрывающем &amp;lt;tex&amp;gt;A[i..j]&amp;lt;/tex&amp;gt;, и принадлежит отрезку &amp;lt;tex&amp;gt;A[i..j]&amp;lt;/tex&amp;gt;, отсюда &amp;lt;tex&amp;gt;RMQ(i, j) = w&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;. Используя [[Сведение задачи LCA к задаче RMQ | алгоритм построения LCA]], получаем:&lt;br /&gt;
препроцессинг для &amp;lt;tex&amp;gt;LCA&amp;lt;/tex&amp;gt; {{---}} &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; и ответ на запрос {{---}} &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&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(1)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В итоге получили &amp;lt;tex&amp;gt;RMQ&amp;lt;/tex&amp;gt; с построением за &amp;lt;tex&amp;gt;O(n)&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;
*[[Сведение задачи LCA к задаче RMQ]]&lt;br /&gt;
*[[Декартово дерево]]&lt;br /&gt;
*[[Алгоритм Фарака-Колтона и Бендера]]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Задача о наименьшем общем предке]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8_LCA_%D0%BA_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B5_RMQ&amp;diff=48222</id>
		<title>Сведение задачи LCA к задаче RMQ</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8_LCA_%D0%BA_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B5_RMQ&amp;diff=48222"/>
				<updated>2015-06-09T10:02:25Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Препроцессинг */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Шаблон:Задача&lt;br /&gt;
|definition = &lt;br /&gt;
Пусть дано корневое дерево &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. На вход подаются запросы вида &amp;lt;tex&amp;gt;(u,\;v)&amp;lt;/tex&amp;gt;, для каждого запроса требуется найти их наименьшего общего предка.&lt;br /&gt;
}}&lt;br /&gt;
{{Определение &lt;br /&gt;
|id=lca_suf_tree&lt;br /&gt;
|definition = '''Наименьшим общим предком''' (англ. ''least common ancestor'') двух узлов &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;T&amp;lt;/tex&amp;gt; называется узел &amp;lt;tex&amp;gt;w&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;, имеет наибольшую глубину.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Алгоритм ==&lt;br /&gt;
=== Идея ===&lt;br /&gt;
Будем решать задачу &amp;lt;tex&amp;gt;LCA&amp;lt;/tex&amp;gt;, уже умея решать задачу &amp;lt;tex&amp;gt;RMQ&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;-того элементов сводится к запросу минимума на отрезке массива, который будет введен позднее.&lt;br /&gt;
&lt;br /&gt;
=== Препроцессинг ===&lt;br /&gt;
Для каждой вершины &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; определим глубину с помощью следующей рекурсивной формулы:&lt;br /&gt;
:&amp;lt;tex&amp;gt;\mathrm{depth}(u) = \begin{cases}&lt;br /&gt;
0 &amp;amp; u = \mathrm{root}(T),\\&lt;br /&gt;
\mathrm{depth}(v) + 1 &amp;amp; u = \mathrm{son}(v).&lt;br /&gt;
\end{cases}&amp;lt;/tex&amp;gt;&lt;br /&gt;
Ясно, что глубина вершины элементарным образом поддерживается во время обхода в глубину.&lt;br /&gt;
&lt;br /&gt;
Запустим [[Обход в глубину, цвета вершин|обход в глубину]] из корня, который будет вычислять значения следующих величин: &lt;br /&gt;
#Cписок глубин посещенных вершин &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;. Глубина текущей вершины добавляется в конец списка при входе в данную вершину, а также после каждого возвращения из её сына.&lt;br /&gt;
#Список посещений узлов &amp;lt;tex&amp;gt;\mathtt{vtx}&amp;lt;/tex&amp;gt;, строящийся аналогично предыдущему, только добавляется не глубина а сама вершина.&lt;br /&gt;
#Значение функции &amp;lt;tex&amp;gt;\mathtt{I}[u]&amp;lt;/tex&amp;gt;, возвращающей индекс в списке глубин &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;, по которому была записана глубина вершины &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; (например на момент входа в вершину).&lt;br /&gt;
&lt;br /&gt;
Вот таким образом будет выглядеть массив &amp;lt;tex&amp;gt;\mathtt{vtx}&amp;lt;/tex&amp;gt; после обхода в глубину:&lt;br /&gt;
&lt;br /&gt;
[[Файл:Полная персистентность.png‎ | мини | left | 500x300px| Пример массива &amp;lt;tex&amp;gt;\mathtt{vtx}&amp;lt;/tex&amp;gt;]]&lt;br /&gt;
&amp;lt;br clear=&amp;quot;all&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Запрос ===&lt;br /&gt;
Будем считать, что &amp;lt;tex&amp;gt;\mathrm{rmq}(d,l,r)&amp;lt;/tex&amp;gt; возвращает индекс минимального элемента в &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; на отрезке &amp;lt;tex&amp;gt;[l..r]&amp;lt;/tex&amp;gt;. Тогда ответом на запрос &amp;lt;tex&amp;gt;\mathrm{lca}(u, v)&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;\mathtt{I}[u] \leqslant \mathtt{I}[v]&amp;lt;/tex&amp;gt;, будет &amp;lt;tex&amp;gt;\mathtt{vtx}[\mathrm{rmq}(d,\mathtt{I}[u], \mathtt{I}[v])]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
=== Доказательство корректности алгоритма ===&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement=&lt;br /&gt;
Наименьшему общему предку вершин &amp;lt;tex&amp;gt;u, v&amp;lt;/tex&amp;gt; соответствует минимальная глубина на отрезке &amp;lt;tex&amp;gt;d[\mathtt{I}[u], \mathtt{I}[v]]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим два узла &amp;lt;tex&amp;gt;u, v&amp;lt;/tex&amp;gt; корневого дерева &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. Рассмотрим отрезок &amp;lt;tex&amp;gt;d[\mathtt{I}[u]..\mathtt{I}[v]]&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;w&amp;lt;/tex&amp;gt; (в дереве есть только один простой путь между вершинами), а следовательно минимум на отрезке никак не больше глубины &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Заметим, что в момент добавления &amp;lt;tex&amp;gt;\mathtt{I}[u]&amp;lt;/tex&amp;gt; обход посещал поддерево с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. В момент добавления &amp;lt;tex&amp;gt;\mathtt{I}[v]&amp;lt;/tex&amp;gt; мы все еще в поддереве с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Значит, и на отрезке между &amp;lt;tex&amp;gt;\mathtt{I}[u]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathtt{I}[v]&amp;lt;/tex&amp;gt; мы находились внутри поддерева с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Отсюда сделаем заключение, что на рассматриваемом отрезке не посещалась вершина, отличная от &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;, с глубиной меньшей либо равной глубины &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;, т. к. подобной вершины нет в поддереве с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}.&lt;br /&gt;
&lt;br /&gt;
== Пример ==&lt;br /&gt;
Рассмотрим дерево на рисунке 1. Найдем наименьшего общего предка вершин, помеченных красным цветом.&lt;br /&gt;
Список глубин, получающийся в результате обхода в глубину {{---}} &amp;lt;tex&amp;gt;[0, 1, 2, 1, 2, 1, 0, 1, 0].&amp;lt;/tex&amp;gt;&lt;br /&gt;
Глубина наименьшего общего предка красных вершин равна минимуму на отрезке &amp;lt;tex&amp;gt;[2, 1, 0, 1].&amp;lt;/tex&amp;gt;&lt;br /&gt;
[[Файл:Lca to rmq.png(2).png|thumb|center|400x200px|Рисунок к примеру]]&lt;br /&gt;
&amp;lt;div style='clear:left;'&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Сложность ==&lt;br /&gt;
&lt;br /&gt;
Для нахождения минимального элемента на отрезке можно использовать [[Алгоритм Фарака-Колтона и Бендера|алгоритм Фарака-Колтона и Бендера]] для &amp;lt;tex&amp;gt;\pm 1RMQ&amp;lt;/tex&amp;gt;, т. к. соседние элементы в списке глубин отличаются не более чем на единицу. Длина списка глубин составляет &amp;lt;tex&amp;gt;(2n - 1)&amp;lt;/tex&amp;gt;, таким образом, препроцессинг работает за &amp;lt;tex&amp;gt;O(n)&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;
*[[Решение RMQ с помощью разреженной таблицы]]&lt;br /&gt;
*[[Алгоритм Фарака-Колтона и Бендера]]&lt;br /&gt;
*[[Сведение задачи RMQ к задаче LCA]]&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
*[http://e-maxx.ru/algo/lca Наименьший общий предок. Нахождение за O (sqrt (N)) и O (log N) с препроцессингом O (N)]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Задача о наименьшем общем предке]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8_LCA_%D0%BA_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B5_RMQ&amp;diff=48221</id>
		<title>Сведение задачи LCA к задаче RMQ</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8_LCA_%D0%BA_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B5_RMQ&amp;diff=48221"/>
				<updated>2015-06-09T10:01:57Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Алгоритм */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Шаблон:Задача&lt;br /&gt;
|definition = &lt;br /&gt;
Пусть дано корневое дерево &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. На вход подаются запросы вида &amp;lt;tex&amp;gt;(u,\;v)&amp;lt;/tex&amp;gt;, для каждого запроса требуется найти их наименьшего общего предка.&lt;br /&gt;
}}&lt;br /&gt;
{{Определение &lt;br /&gt;
|id=lca_suf_tree&lt;br /&gt;
|definition = '''Наименьшим общим предком''' (англ. ''least common ancestor'') двух узлов &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;T&amp;lt;/tex&amp;gt; называется узел &amp;lt;tex&amp;gt;w&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;, имеет наибольшую глубину.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Алгоритм ==&lt;br /&gt;
=== Идея ===&lt;br /&gt;
Будем решать задачу &amp;lt;tex&amp;gt;LCA&amp;lt;/tex&amp;gt;, уже умея решать задачу &amp;lt;tex&amp;gt;RMQ&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;-того элементов сводится к запросу минимума на отрезке массива, который будет введен позднее.&lt;br /&gt;
&lt;br /&gt;
=== Препроцессинг ===&lt;br /&gt;
Для каждой вершины &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; определим глубину с помощью следующей рекурсивной формулы:&lt;br /&gt;
:&amp;lt;tex&amp;gt;\mathrm{depth}(u) = \begin{cases}&lt;br /&gt;
0 &amp;amp; u = \mathrm{root}(T),\\&lt;br /&gt;
\mathrm{depth}(v) + 1 &amp;amp; u = \mathrm{son}(v).&lt;br /&gt;
\end{cases}&amp;lt;/tex&amp;gt;&lt;br /&gt;
Ясно, что глубина вершины элементарным образом поддерживается во время обхода в глубину.&lt;br /&gt;
&lt;br /&gt;
Запустим [[Обход в глубину, цвета вершин|обход в глубину]] из корня, который будет вычислять значения следующих величин: &lt;br /&gt;
#Cписок глубин посещенных вершин &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;. Глубина текущей вершины добавляется в конец списка при входе в данную вершину, а также после каждого возвращения из её сына.&lt;br /&gt;
#Список посещений узлов &amp;lt;tex&amp;gt;\mathtt{vtx}&amp;lt;/tex&amp;gt;, строящийся аналогично предыдущему, только добавляется не глубина а сама вершина.&lt;br /&gt;
#Значение функции &amp;lt;tex&amp;gt;\mathtt{I}[u]&amp;lt;/tex&amp;gt;, возвращающей индекс в списке глубин &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;, по которому была записана глубина вершины &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; (например на момент входа в вершину).&lt;br /&gt;
&lt;br /&gt;
Вот таким образом будет выглядеть массив &amp;lt;tex&amp;gt;\mathtt{vtx}&amp;lt;/tex&amp;gt; после обхода в глубину:&lt;br /&gt;
&lt;br /&gt;
[[Файл:Полная персистентность.png‎ | мини | left | 500x200px| Пример массива &amp;lt;tex&amp;gt;\mathtt{vtx}&amp;lt;/tex&amp;gt;]]&lt;br /&gt;
&amp;lt;br clear=&amp;quot;all&amp;quot;&amp;gt;&lt;br /&gt;
=== Запрос ===&lt;br /&gt;
Будем считать, что &amp;lt;tex&amp;gt;\mathrm{rmq}(d,l,r)&amp;lt;/tex&amp;gt; возвращает индекс минимального элемента в &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; на отрезке &amp;lt;tex&amp;gt;[l..r]&amp;lt;/tex&amp;gt;. Тогда ответом на запрос &amp;lt;tex&amp;gt;\mathrm{lca}(u, v)&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;\mathtt{I}[u] \leqslant \mathtt{I}[v]&amp;lt;/tex&amp;gt;, будет &amp;lt;tex&amp;gt;\mathtt{vtx}[\mathrm{rmq}(d,\mathtt{I}[u], \mathtt{I}[v])]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
=== Доказательство корректности алгоритма ===&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement=&lt;br /&gt;
Наименьшему общему предку вершин &amp;lt;tex&amp;gt;u, v&amp;lt;/tex&amp;gt; соответствует минимальная глубина на отрезке &amp;lt;tex&amp;gt;d[\mathtt{I}[u], \mathtt{I}[v]]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим два узла &amp;lt;tex&amp;gt;u, v&amp;lt;/tex&amp;gt; корневого дерева &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. Рассмотрим отрезок &amp;lt;tex&amp;gt;d[\mathtt{I}[u]..\mathtt{I}[v]]&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;w&amp;lt;/tex&amp;gt; (в дереве есть только один простой путь между вершинами), а следовательно минимум на отрезке никак не больше глубины &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Заметим, что в момент добавления &amp;lt;tex&amp;gt;\mathtt{I}[u]&amp;lt;/tex&amp;gt; обход посещал поддерево с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. В момент добавления &amp;lt;tex&amp;gt;\mathtt{I}[v]&amp;lt;/tex&amp;gt; мы все еще в поддереве с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Значит, и на отрезке между &amp;lt;tex&amp;gt;\mathtt{I}[u]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathtt{I}[v]&amp;lt;/tex&amp;gt; мы находились внутри поддерева с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Отсюда сделаем заключение, что на рассматриваемом отрезке не посещалась вершина, отличная от &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;, с глубиной меньшей либо равной глубины &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;, т. к. подобной вершины нет в поддереве с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}.&lt;br /&gt;
&lt;br /&gt;
== Пример ==&lt;br /&gt;
Рассмотрим дерево на рисунке 1. Найдем наименьшего общего предка вершин, помеченных красным цветом.&lt;br /&gt;
Список глубин, получающийся в результате обхода в глубину {{---}} &amp;lt;tex&amp;gt;[0, 1, 2, 1, 2, 1, 0, 1, 0].&amp;lt;/tex&amp;gt;&lt;br /&gt;
Глубина наименьшего общего предка красных вершин равна минимуму на отрезке &amp;lt;tex&amp;gt;[2, 1, 0, 1].&amp;lt;/tex&amp;gt;&lt;br /&gt;
[[Файл:Lca to rmq.png(2).png|thumb|center|400x200px|Рисунок к примеру]]&lt;br /&gt;
&amp;lt;div style='clear:left;'&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Сложность ==&lt;br /&gt;
&lt;br /&gt;
Для нахождения минимального элемента на отрезке можно использовать [[Алгоритм Фарака-Колтона и Бендера|алгоритм Фарака-Колтона и Бендера]] для &amp;lt;tex&amp;gt;\pm 1RMQ&amp;lt;/tex&amp;gt;, т. к. соседние элементы в списке глубин отличаются не более чем на единицу. Длина списка глубин составляет &amp;lt;tex&amp;gt;(2n - 1)&amp;lt;/tex&amp;gt;, таким образом, препроцессинг работает за &amp;lt;tex&amp;gt;O(n)&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;
*[[Решение RMQ с помощью разреженной таблицы]]&lt;br /&gt;
*[[Алгоритм Фарака-Колтона и Бендера]]&lt;br /&gt;
*[[Сведение задачи RMQ к задаче LCA]]&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
*[http://e-maxx.ru/algo/lca Наименьший общий предок. Нахождение за O (sqrt (N)) и O (log N) с препроцессингом O (N)]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Задача о наименьшем общем предке]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8_LCA_%D0%BA_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B5_RMQ&amp;diff=48220</id>
		<title>Сведение задачи LCA к задаче RMQ</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8_LCA_%D0%BA_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B5_RMQ&amp;diff=48220"/>
				<updated>2015-06-09T10:01:04Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Препроцессинг */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Шаблон:Задача&lt;br /&gt;
|definition = &lt;br /&gt;
Пусть дано корневое дерево &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. На вход подаются запросы вида &amp;lt;tex&amp;gt;(u,\;v)&amp;lt;/tex&amp;gt;, для каждого запроса требуется найти их наименьшего общего предка.&lt;br /&gt;
}}&lt;br /&gt;
{{Определение &lt;br /&gt;
|id=lca_suf_tree&lt;br /&gt;
|definition = '''Наименьшим общим предком''' (англ. ''least common ancestor'') двух узлов &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;T&amp;lt;/tex&amp;gt; называется узел &amp;lt;tex&amp;gt;w&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;, имеет наибольшую глубину.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Алгоритм ==&lt;br /&gt;
=== Идея ===&lt;br /&gt;
Будем решать задачу &amp;lt;tex&amp;gt;LCA&amp;lt;/tex&amp;gt;, уже умея решать задачу &amp;lt;tex&amp;gt;RMQ&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;-того элементов сводится к запросу минимума на отрезке массива, который будет введен позднее.&lt;br /&gt;
&lt;br /&gt;
=== Препроцессинг ===&lt;br /&gt;
Для каждой вершины &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; определим глубину с помощью следующей рекурсивной формулы:&lt;br /&gt;
:&amp;lt;tex&amp;gt;\mathrm{depth}(u) = \begin{cases}&lt;br /&gt;
0 &amp;amp; u = \mathrm{root}(T),\\&lt;br /&gt;
\mathrm{depth}(v) + 1 &amp;amp; u = \mathrm{son}(v).&lt;br /&gt;
\end{cases}&amp;lt;/tex&amp;gt;&lt;br /&gt;
Ясно, что глубина вершины элементарным образом поддерживается во время обхода в глубину.&lt;br /&gt;
&lt;br /&gt;
Запустим [[Обход в глубину, цвета вершин|обход в глубину]] из корня, который будет вычислять значения следующих величин: &lt;br /&gt;
#Cписок глубин посещенных вершин &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;. Глубина текущей вершины добавляется в конец списка при входе в данную вершину, а также после каждого возвращения из её сына.&lt;br /&gt;
#Список посещений узлов &amp;lt;tex&amp;gt;\mathtt{vtx}&amp;lt;/tex&amp;gt;, строящийся аналогично предыдущему, только добавляется не глубина а сама вершина.&lt;br /&gt;
#Значение функции &amp;lt;tex&amp;gt;\mathtt{I}[u]&amp;lt;/tex&amp;gt;, возвращающей индекс в списке глубин &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;, по которому была записана глубина вершины &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; (например на момент входа в вершину).&lt;br /&gt;
&lt;br /&gt;
Вот таким образом будет выглядеть массив &amp;lt;tex&amp;gt;\mathtt{vtx}&amp;lt;/tex&amp;gt; после обхода в глубину:&lt;br /&gt;
&lt;br /&gt;
[[Файл:Полная персистентность.png‎ | мини | left | 500x200px| Пример массива &amp;lt;tex&amp;gt;\mathtt{vtx}&amp;lt;/tex&amp;gt;]]&lt;br /&gt;
&lt;br /&gt;
=== Запрос ===&lt;br /&gt;
Будем считать, что &amp;lt;tex&amp;gt;\mathrm{rmq}(d,l,r)&amp;lt;/tex&amp;gt; возвращает индекс минимального элемента в &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; на отрезке &amp;lt;tex&amp;gt;[l..r]&amp;lt;/tex&amp;gt;. Тогда ответом на запрос &amp;lt;tex&amp;gt;\mathrm{lca}(u, v)&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;\mathtt{I}[u] \leqslant \mathtt{I}[v]&amp;lt;/tex&amp;gt;, будет &amp;lt;tex&amp;gt;\mathtt{vtx}[\mathrm{rmq}(d,\mathtt{I}[u], \mathtt{I}[v])]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Доказательство корректности алгоритма ===&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement=&lt;br /&gt;
Наименьшему общему предку вершин &amp;lt;tex&amp;gt;u, v&amp;lt;/tex&amp;gt; соответствует минимальная глубина на отрезке &amp;lt;tex&amp;gt;d[\mathtt{I}[u], \mathtt{I}[v]]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим два узла &amp;lt;tex&amp;gt;u, v&amp;lt;/tex&amp;gt; корневого дерева &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. Рассмотрим отрезок &amp;lt;tex&amp;gt;d[\mathtt{I}[u]..\mathtt{I}[v]]&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;w&amp;lt;/tex&amp;gt; (в дереве есть только один простой путь между вершинами), а следовательно минимум на отрезке никак не больше глубины &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Заметим, что в момент добавления &amp;lt;tex&amp;gt;\mathtt{I}[u]&amp;lt;/tex&amp;gt; обход посещал поддерево с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. В момент добавления &amp;lt;tex&amp;gt;\mathtt{I}[v]&amp;lt;/tex&amp;gt; мы все еще в поддереве с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Значит, и на отрезке между &amp;lt;tex&amp;gt;\mathtt{I}[u]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathtt{I}[v]&amp;lt;/tex&amp;gt; мы находились внутри поддерева с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Отсюда сделаем заключение, что на рассматриваемом отрезке не посещалась вершина, отличная от &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;, с глубиной меньшей либо равной глубины &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;, т. к. подобной вершины нет в поддереве с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}.&lt;br /&gt;
&lt;br /&gt;
== Пример ==&lt;br /&gt;
Рассмотрим дерево на рисунке 1. Найдем наименьшего общего предка вершин, помеченных красным цветом.&lt;br /&gt;
Список глубин, получающийся в результате обхода в глубину {{---}} &amp;lt;tex&amp;gt;[0, 1, 2, 1, 2, 1, 0, 1, 0].&amp;lt;/tex&amp;gt;&lt;br /&gt;
Глубина наименьшего общего предка красных вершин равна минимуму на отрезке &amp;lt;tex&amp;gt;[2, 1, 0, 1].&amp;lt;/tex&amp;gt;&lt;br /&gt;
[[Файл:Lca to rmq.png(2).png|thumb|center|400x200px|Рисунок к примеру]]&lt;br /&gt;
&amp;lt;div style='clear:left;'&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Сложность ==&lt;br /&gt;
&lt;br /&gt;
Для нахождения минимального элемента на отрезке можно использовать [[Алгоритм Фарака-Колтона и Бендера|алгоритм Фарака-Колтона и Бендера]] для &amp;lt;tex&amp;gt;\pm 1RMQ&amp;lt;/tex&amp;gt;, т. к. соседние элементы в списке глубин отличаются не более чем на единицу. Длина списка глубин составляет &amp;lt;tex&amp;gt;(2n - 1)&amp;lt;/tex&amp;gt;, таким образом, препроцессинг работает за &amp;lt;tex&amp;gt;O(n)&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;
*[[Решение RMQ с помощью разреженной таблицы]]&lt;br /&gt;
*[[Алгоритм Фарака-Колтона и Бендера]]&lt;br /&gt;
*[[Сведение задачи RMQ к задаче LCA]]&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
*[http://e-maxx.ru/algo/lca Наименьший общий предок. Нахождение за O (sqrt (N)) и O (log N) с препроцессингом O (N)]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Задача о наименьшем общем предке]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=48218</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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=48218"/>
				<updated>2015-06-09T09:21:58Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* База рекурсии */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Алгоритм Карккайнена-Сандерса''' (Kärkkäinen, Sanders) — алгоритм построения [[суффиксный массив | суффиксного массива]] за линейное время.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Используемые обозначения ==&lt;br /&gt;
* В данном конспекте используется 0-индексация.&lt;br /&gt;
* &amp;lt;tex&amp;gt;S[i..j] &amp;lt;/tex&amp;gt; — подстрока строки &amp;lt;tex&amp;gt; S &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;-й символы включительно.&lt;br /&gt;
* Пусть длина строки &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;. Обозначим &amp;lt;tex&amp;gt; S^*[i..j] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; j \geqslant n &amp;lt;/tex&amp;gt; как строку &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt;, дополненную защитными символами &amp;lt;tex&amp;gt; \$ &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;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Четным суффиксом''' назовем суффикс, начинающийся в четной позиции. &amp;lt;br&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;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left( \dfrac{n}{2}\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для упрощения алгоритма вначале дополним нашу строку до четной длины (например, добавлением &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; в конец). На шаге слияния мы сможем избавиться от него.&lt;br /&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;
&lt;br /&gt;
# Отобразим исходную строку &amp;lt;tex&amp;gt; S^* &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; длины  &amp;lt;tex&amp;gt; \dfrac{n}{2} &amp;lt;/tex&amp;gt;  следующим образом:&lt;br /&gt;
#* Сделаем список, состоящий из пар символов вида &amp;lt;tex&amp;gt; S^*[i..i + 1] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; i \bmod 2 = 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его цифровой сортировкой за линейное время и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S^*[1..n] &amp;lt;/tex&amp;gt; в алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;, получив строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; половинной длины.&lt;br /&gt;
# Рекурсивно построим суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Построим суффиксный массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;. Очевидно, &amp;lt;tex&amp;gt; A_{S_o}[i] = 2 A_{S'}[i] + 1 &amp;lt;/tex&amp;gt;, так отношение упорядоченности любых двух строк в старом алфавите &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt; эквивалентно отношению упорядоченности в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; по его построению.&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для четных суффиксов=====&lt;br /&gt;
На этом шаге мы за линейное время получим суффиксный массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; для четных суффиксов, используя уже построенный &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что сортировка множества четных суффиксов &amp;lt;tex&amp;gt; \{ S^*[i..n] \mid i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке множества пар &amp;lt;tex&amp;gt; \{ (S^*[i], S^*[i+1..n]) \mid i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt;. Однако &amp;lt;tex&amp;gt; S^*[i+1..n] &amp;lt;/tex&amp;gt; — нечетный суффикс, и его относительную позицию мы уже узнали на шаге 1.&lt;br /&gt;
&lt;br /&gt;
Таким образом, чтобы отсортировать эти пары за линейное время, сначала сразу выпишем их в порядке возрастания второго элемента пары (то есть в порядке вхождения в массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;), а потом отсортируем устойчивой сортировкой подсчетом по первым элементам. После этого легко можно восстановить массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n / 2 - 1&lt;br /&gt;
     M.add(Pair(S[&amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 quick_stable_sort(M)&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
    &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&gt;
&lt;br /&gt;
Заметим, что массив &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; перед сортировкой подсчетом не был явно отсортирован по вторым элементам, и хранил не суффиксы, а их позиции в строке &amp;lt;tex&amp;gt; S &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;
Для суффиксного дерева третий шаг алгоритма опирается на специфические особенности суффиксных деревьев, которые не присущи суффиксным массивам&amp;lt;ref&amp;gt;M. Farach. Optimal suffix tree construction with large alphabets. http://www.cs.rutgers.edu/~farach/pubs/FarFerrMuthu00.pdf &amp;lt;/ref&amp;gt; .  &lt;br /&gt;
В случае [[Суффиксный массив|суффиксного массива]] слияние становится очень сложным, но все же оно было реализовано в алгоритме Ким-Сим-Парк-Парка&amp;lt;ref&amp;gt; D. K. Kim, J. S. Sim, H. Park, and K. Park. Linear-time construction of suffix arrays. http://www.springerlink.com/content/568156021q45r320/&amp;lt;/ref&amp;gt;. Однако простой модификацией алгоритма можно значительно упростить его.&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
Покажем первые два шага агоритма для строки '''ababbbaa'''.&lt;br /&gt;
&lt;br /&gt;
Во-первых, добавив защитный символ '''$''', получив строку '''ababbbaa$''' (для этого алгоритма он не требуется, но может понадобиться в применениях суффиксного массива). Во-вторых, дополним ее до четной длины, получив '''ababbbaa$$'''.&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для нечетных суффиксов=====&lt;br /&gt;
# В новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет четыре элемента — '''ba''', '''bb''', '''a$''', '''$$'''. После сортировки они получат номера 2, 3, 1 и 0 соответственно.&lt;br /&gt;
# Переводим строку &amp;lt;tex&amp;gt;S^*[1..n]&amp;lt;/tex&amp;gt; = '''babbbaa$$$''' в новый алфавит. Сжатой строкой &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; будет '''23210'''.&lt;br /&gt;
# После рекурсивного вызова получим, что &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [4, 3, 2, 0, 1], и &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; = [9, 7, 5, 1, 3].&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для четных суффиксов=====&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [(&amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt;, 9), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 7), (&amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, 5), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 1), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 3)].&lt;br /&gt;
# После сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt;= [(&amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt;, 9), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 7), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 1), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 3), (&amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, 5)].&lt;br /&gt;
# Восстановив массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;, получаем [8, 6, 0, 2, 4], что действительно является суффиксным массивом для четных суффиксов.&lt;br /&gt;
&lt;br /&gt;
=====Слияние суффиксных массивов=====&lt;br /&gt;
Если бы мы умели сливать &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; за линейное время, получили бы:&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 9 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; a\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; aa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ababbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; baa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; babbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Как и было сказано вначале, избавиться от лишних '''$''' легко, так как суффиксы, им соответствующие, будут первыми в суффиксном массиве (в данном случае достаточно выбросить &amp;quot;9&amp;quot; из суффиксного массива).&lt;br /&gt;
&lt;br /&gt;
== Алгоритм Карккайнена-Сандерса == &lt;br /&gt;
Изменим изначальный алгоритм следующим образом:&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих позициям, не кратным трем. Рекурсивно сведем это к построению суффиксного массива для строки длиной в две трети исходной.&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих кратным трем позициям, используя результат первого шага, за линейное время.&lt;br /&gt;
# Сольем эти суффиксные массивы в один за линейное время.&lt;br /&gt;
&lt;br /&gt;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left(\dfrac23 n\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого также является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt; (это видно из того, что сумма геометрической прогрессии с основанием &amp;lt;tex&amp;gt; \dfrac23 &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;).&lt;br /&gt;
&lt;br /&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;S^*&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* '''База рекурсии'''&lt;br /&gt;
Для этого алгоритма минимальной базой рекурсии будет строка длиной 4, так как она дополняется до длины 6, после чего вновь следует рекурсивный вызов строки длиной 4, и, если бы база была меньше 4, алгоритм вошел бы в бесконечную рекурсию. На этом этапе также можно применить обычную сортировку суффиксов, так как это потребует &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
На этом шаге строится суффиксный массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; для множества суффиксов &amp;lt;tex&amp;gt; \{ S^*[i..n-1] \mid i \bmod 3 \ne 0 \} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Получим строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; аналогично предыдущему алгоритму:&lt;br /&gt;
#* Сделаем список, состоящий из троек &amp;lt;tex&amp;gt; S^*[i..i+2]&amp;lt;/tex&amp;gt; , где &amp;lt;tex&amp;gt; i \bmod 3 \ne 0 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его за линейное время цифровой сортировкой и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S^*[1..n]S^*[2..n+1] &amp;lt;/tex&amp;gt; в строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; длиной &amp;lt;tex&amp;gt; \dfrac23 n &amp;lt;/tex&amp;gt; в алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;. Тогда суффиксу &amp;lt;tex&amp;gt; S^*[i..n-1] &amp;lt;/tex&amp;gt; в старом алфавите, где &amp;lt;tex&amp;gt; i \bmod 3 = 1 &amp;lt;/tex&amp;gt;, в новом алфавите будет соответствовать строка &amp;lt;tex&amp;gt; S'\left[\dfrac{i-1}{3}..\dfrac{n}{3} - 1\right] &amp;lt;/tex&amp;gt;, а если &amp;lt;tex&amp;gt; i\bmod 3 = 2 &amp;lt;/tex&amp;gt;, то строка &amp;lt;tex&amp;gt; S'\left[\dfrac{n}{3} + \dfrac{i-2}{3}..\dfrac{2n}{3} - 1\right] &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Вызовем алгоритм рекурсивно для строки &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt;, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Пройдем по массиву &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt; A_{S'}[i] &amp;lt; \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j = 3A_{S'}[i] + 1 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;, если же &amp;lt;tex&amp;gt; A_{S'}[i] \geqslant \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j =  3\left(A_{S'}[i] - \dfrac{n}{3}\right) + 2 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S^* &amp;lt;/tex&amp;gt;. Псевдокод получения &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;.length - 1:&lt;br /&gt;
    '''if''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] &amp;lt; n/3:&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] + 1)&lt;br /&gt;
    '''else''':&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * (&amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] - n/3) + 2)&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
Этот шаг также аналогичен первой версии алгоритма. Сортировка множества &amp;lt;tex&amp;gt; \{ S^*[i..n-1] \mid i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке пар &amp;lt;tex&amp;gt; \{ (S^*[i], S^*[i+1..n-1]) \mid i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; S^*[i+1..n-1] &amp;lt;/tex&amp;gt; — суффиксы в позициях, равных 1 по модулю 3, относительный порядок которых уже известен. Выпишем эти пары в порядке вхождения их в &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и отсортируем по первому элементу устойчивой сортировкой подсчетом, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;. Псевдокод этого шага:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' 2n/3 - 1:&lt;br /&gt;
     '''if''' &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[i] % 3 == 1:&lt;br /&gt;
         M.add(Pair(S*[&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 '''for''' i = 0 '''to''' n/3 - 1:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&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; A_{S_0} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, чтобы получить суффиксный массив &amp;lt;tex&amp;gt; A_{S} &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;
&lt;br /&gt;
Пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 1 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Это аналогично сравнению пар &amp;lt;tex&amp;gt; (S^*[i], S^*[i+1..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S^*[j], S^*[j+1..n-1]) &amp;lt;/tex&amp;gt;. Сравнить первые элементы пар мы можем за &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt;, а относительный порядок вторых элементов пар нам уже известен, так как они соответствуют позициям, равным 2 и 1 по модулю 3 соответственно.&lt;br /&gt;
&lt;br /&gt;
Аналогично, пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 2 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Тогда это аналогично сравнению троек &amp;lt;tex&amp;gt; (S^*[i], S^*[i+1], S^*[i+2..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S^*[j], S^*[j+1], S^*[j+2..n-1]) &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;A_{S}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 &amp;lt;font color=green&amp;gt;// Вначале предподсчитаем за O(n) обратную перестановку для суффиксного массива &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;, то есть массив rank такой, что &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[rank[i]] = i.&lt;br /&gt;
 // Тогда мы сможем за O(1) сравнивать суффиксы по их позиции. &amp;lt;/font&amp;gt;&lt;br /&gt;
 rank = inverse(&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;) &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3 '''and''' j &amp;lt; n/3:&lt;br /&gt;
     pos12 = &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i]&lt;br /&gt;
     pos0  = &amp;lt;tex&amp;gt; A_{0} &amp;lt;/tex&amp;gt;[j]&lt;br /&gt;
     '''if''' pos12 % 3 == 1:&lt;br /&gt;
        ''' if''' Pair(S*[pos12], rank[pos12 + 1]) &amp;lt; Pair(S*[pos0], rank[pos0 + 1]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++  &lt;br /&gt;
     '''else''':&lt;br /&gt;
         '''if''' Triple(S*[pos12], S*[pos12 + 1], rank[pos12 + 2]) &amp;lt; Triple(S*[pos0], S*[pos0 + 1], rank[pos0 + 2]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++ &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i])&lt;br /&gt;
     i++&lt;br /&gt;
 '''while''' j &amp;lt; n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{0}} &amp;lt;/tex&amp;gt;[j])&lt;br /&gt;
     j++&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;
Построим суффиксный массив для строки '''abbacab'''. После добавления защитного символа и дополнения до кратной трем длины, получим '''abbacab$$'''.&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
# Тройками, соответствующими равными 1 по модулю 3 позициям, будут: '''bba''', '''cab''', '''$$$''', соответствующими равным 2 по модулю 3 — '''bac''', '''cab$''', '''$$$'''. Новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет содержать элементы '''bba''', '''cab''', '''$$$''', '''bac''', '''ab$''', которые после сортировки получат номера 3, 4, 0, 2, 1 соответственно.&lt;br /&gt;
# Строкой '''bbacab$$$bacab$$$$''' в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; = 340210. &lt;br /&gt;
# После рекурсивного вызова получим &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [5, 2, 4, 3, 0, 1]. Пересчитав &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, получим [(5 - 3)*3 + 2, 2 * 3 + 1, (4 - 3) * 3 + 2, (3 - 3) * 3 + 2, 0 * 3 + 1, 1 * 3 + 1] = [8, 7, 5, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и выбрав в нем элементы, равные 1 по модулю 3, получим массив пар &amp;lt;tex&amp;gt;M&amp;lt;/tex&amp;gt; = [(&amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, 7), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 1), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 4)]&lt;br /&gt;
# После устойчивой сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''a''', 1), ('''a''', 4), ('''b''', 7)]&lt;br /&gt;
# Восстановив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;, получаем [0, 3, 6].&lt;br /&gt;
* '''Слияние суффиксных массивов'''&lt;br /&gt;
Рассмотрим, к примеру, третью итерацию слияния, к этой итерации массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 2, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0, на ней мы сливаем суффиксы, соответствующие позициям 5 и 0. &lt;br /&gt;
# Образуем тройки &amp;lt;tex&amp;gt;(S^*[5], S^*[6], S^*[7..8])&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;(S^*[0], S^*[1], S^*[2..8])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
# После получения относительного порядка суффиксов, получим тройки ('''a''', '''b''', 1) и ('''a''', '''b''', 3). Первая тройка меньше второй, поэтому добавляем суффикс, соответствующий позиции 5 в массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# В конце итерации получаем &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 3, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0.&lt;br /&gt;
К концу слияния получим &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5, 0, 3, 6, 2, 1, 4]. Так как мы добавляли один символ &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; в начале алгоритма для дополнения строки до длины, кратной трем, выбросим последний суффикс из &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;, получим в итоге, что &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [7, 5, 0, 3, 6, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; acab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; b\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; cab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
Заметим, что № 9 будет выброшен, так как в начале алгоритма был добавлен один &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt; к строке&lt;br /&gt;
 &lt;br /&gt;
{|&lt;br /&gt;
| [[Файл:Kark_sanders_stage1.png|325px|thumb| Фаза 1]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage2.png|325px|thumb| Фаза 2]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage3.png|325px|thumb| Фаза 3]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Обобщение алгоритма ==&lt;br /&gt;
Массив LCP можно получить за линейное время [[Алгоритм_Касаи_и_др. | алгоритмом Касаи]].&lt;br /&gt;
&lt;br /&gt;
На самом деле, алгоритм можно обобщить&amp;lt;ref name=&amp;quot;generalisation&amp;quot;&amp;gt; Juha Kärkkäinen, Peter Sanders and Stefan Burkhardt. Linear work suffix array construction. http://www.cs.helsinki.fi/juha.karkkainen/publications/jacm05-revised.pdf &amp;lt;/ref&amp;gt;, взяв на первом шаге, к примеру, суффиксы, позиции которых по модулю 7 дают 3, 5 и 6. Для этого потребуются некоторое усложнение алгоритма, например, сортировка оставшихся суффиксов в нескольких группах на шаге 2 и слияние нескольких групп на шаге 3, но основная идея алгоритма остается той же. Множества, которые можно выбрать, на первом шаге определяются '''разностным покрытием''' (''difference cover'').&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Разностное покрытие''' (англ. ''difference cover'') &amp;lt;tex&amp;gt; D &amp;lt;/tex&amp;gt; по модулю &amp;lt;tex&amp;gt;m &amp;lt;/tex&amp;gt; — множество чисел от &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;m - 1 &amp;lt;/tex&amp;gt; таких, что &amp;lt;tex&amp;gt; \forall i \in [0, m-1]: \exists j, k \in D: i \equiv k - j \pmod m &amp;lt;/tex&amp;gt;.&lt;br /&gt;
}} &lt;br /&gt;
Например, &amp;lt;tex&amp;gt; \{1, 2\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt; \{3, 5, 6\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt; \{1\} &amp;lt;/tex&amp;gt; — не является разностным покрытием по модулю &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;, поэтому этот алгоритм не применим к нему. Подробнее узнать, как вычислять разностное покрытие для заданного модуля можно также здесь&amp;lt;ref name=&amp;quot;generalisation&amp;quot;/&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Источники информации==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
* [http://www.cs.helsinki.fi/juha.karkkainen/publications/icalp03.pdf Juha Kärkkäinen and Peter Sanders. Simple linear work suffix array construction]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Суффиксный массив]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8_RMQ_%D0%BA_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B5_LCA&amp;diff=48215</id>
		<title>Сведение задачи RMQ к задаче LCA</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8_RMQ_%D0%BA_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B5_LCA&amp;diff=48215"/>
				<updated>2015-06-09T07:24:49Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Описание */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Шаблон:Задача&lt;br /&gt;
|definition =Дан массив &amp;lt;tex&amp;gt;A[1..N]&amp;lt;/tex&amp;gt;. Поступают запросы вида &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, на каждый запрос требуется найти минимум в массиве &amp;lt;tex&amp;gt;A&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;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Алгоритм ==&lt;br /&gt;
===Описание===&lt;br /&gt;
[[Файл:Wiki.PNG|thumb|right|400x200px|Пример декартового дерева]]&lt;br /&gt;
Будем решать задачу RMQ, уже умея решать задачу LCA. Для хранения решения задачи о LCA будем использовать  [[декартово дерево по неявному ключу]]. Тогда минимум на отрезке от &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;i&amp;lt;/tex&amp;gt;-того и &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-того элементов из декартова дерева. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Декартово дерево по неявному ключу на массиве &amp;lt;tex&amp;gt;A[1..N]&amp;lt;/tex&amp;gt; {{---}} это бинарное дерево, допускающее следующее рекурсивное построение:&lt;br /&gt;
* Корнем дерева является элемент массива, имеющий минимальное значение &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;, скажем &amp;lt;tex&amp;gt;A[i]&amp;lt;/tex&amp;gt;. Если минимальных элементов несколько, можно взять любой.&lt;br /&gt;
* Левым поддеревом является декартово дерево на массиве &amp;lt;tex&amp;gt;A[1..i-1]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
* Правым поддеревом является декартово дерево на массиве &amp;lt;tex&amp;gt;A[i+1..N]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Здесь и далее &amp;lt;tex&amp;gt;A[i]&amp;lt;/tex&amp;gt; будет также использоваться для обозначения соответствующей вершины дерева.&lt;br /&gt;
&lt;br /&gt;
Построим декартово дерево на массиве &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;. Тогда &amp;lt;tex&amp;gt;RMQ(i, j)&amp;lt;/tex&amp;gt; = &amp;lt;tex&amp;gt;LCA(A[i], A[j])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&amp;lt;br clear=&amp;quot;all&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Корректность ===&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement=&lt;br /&gt;
&amp;lt;tex&amp;gt;RMQ(i, j)&amp;lt;/tex&amp;gt; = &amp;lt;tex&amp;gt;LCA(A[i], A[j])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
Положим &amp;lt;tex&amp;gt;w = LCA(A[i], A[j])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что &amp;lt;tex&amp;gt;A[i]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;A[j]&amp;lt;/tex&amp;gt; не принадлежат одновременно либо правому, либо левому поддереву &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;, потому как тогда бы соответствующий сын находился на большей глубине, чем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;, и также являлся предком как &amp;lt;tex&amp;gt;A[i]&amp;lt;/tex&amp;gt; так и &amp;lt;tex&amp;gt;A[j]&amp;lt;/tex&amp;gt;, что противоречит определению &amp;lt;tex&amp;gt;LCA&amp;lt;/tex&amp;gt;. Из этого замечанию следует, что &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt; лежит между &amp;lt;tex&amp;gt;A[i]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;A[j]&amp;lt;/tex&amp;gt; и, следовательно, принадлежит отрезку &amp;lt;tex&amp;gt;A[i..j]&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;w&amp;lt;/tex&amp;gt; содержит в себе подмассив &amp;lt;tex&amp;gt;A[i..j]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Суммируя, получаем, что &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt; имеет минимальное значение на отрезке, покрывающем &amp;lt;tex&amp;gt;A[i..j]&amp;lt;/tex&amp;gt;, и принадлежит отрезку &amp;lt;tex&amp;gt;A[i..j]&amp;lt;/tex&amp;gt;, отсюда &amp;lt;tex&amp;gt;RMQ(i, j) = w&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;. Используя [[Сведение задачи LCA к задаче RMQ | алгоритм построения LCA]], получаем:&lt;br /&gt;
препроцессинг для &amp;lt;tex&amp;gt;LCA&amp;lt;/tex&amp;gt; {{---}} &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; и ответ на запрос {{---}} &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&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(1)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В итоге получили &amp;lt;tex&amp;gt;RMQ&amp;lt;/tex&amp;gt; с построением за &amp;lt;tex&amp;gt;O(n)&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;
*[[Сведение задачи LCA к задаче RMQ]]&lt;br /&gt;
*[[Декартово дерево]]&lt;br /&gt;
*[[Алгоритм Фарака-Колтона и Бендера]]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Задача о наименьшем общем предке]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8_LCA_%D0%BA_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B5_RMQ&amp;diff=48214</id>
		<title>Сведение задачи LCA к задаче RMQ</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8_LCA_%D0%BA_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B5_RMQ&amp;diff=48214"/>
				<updated>2015-06-09T07:24:20Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Пример */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Шаблон:Задача&lt;br /&gt;
|definition = &lt;br /&gt;
Пусть дано корневое дерево &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. На вход подаются запросы вида &amp;lt;tex&amp;gt;(u,\;v)&amp;lt;/tex&amp;gt;, для каждого запроса требуется найти их наименьшего общего предка.&lt;br /&gt;
}}&lt;br /&gt;
{{Определение &lt;br /&gt;
|id=lca_suf_tree&lt;br /&gt;
|definition = '''Наименьшим общим предком''' (англ. ''least common ancestor'') двух узлов &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;T&amp;lt;/tex&amp;gt; называется узел &amp;lt;tex&amp;gt;w&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;, имеет наибольшую глубину.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Алгоритм ==&lt;br /&gt;
=== Идея ===&lt;br /&gt;
Будем решать задачу &amp;lt;tex&amp;gt;LCA&amp;lt;/tex&amp;gt;, уже умея решать задачу &amp;lt;tex&amp;gt;RMQ&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;-того элементов сводится к запросу минимума на отрезке массива, который будет введен позднее.&lt;br /&gt;
&lt;br /&gt;
=== Препроцессинг ===&lt;br /&gt;
Для каждой вершины &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; определим глубину с помощью следующей рекурсивной формулы:&lt;br /&gt;
:&amp;lt;tex&amp;gt;\mathrm{depth}(u) = \begin{cases}&lt;br /&gt;
0 &amp;amp; u = \mathrm{root}(T),\\&lt;br /&gt;
\mathrm{depth}(v) + 1 &amp;amp; u = \mathrm{son}(v).&lt;br /&gt;
\end{cases}&amp;lt;/tex&amp;gt;&lt;br /&gt;
Ясно, что глубина вершины элементарным образом поддерживается во время обхода в глубину.&lt;br /&gt;
&lt;br /&gt;
Запустим [[Обход в глубину, цвета вершин|обход в глубину]] из корня, который будет вычислять значения следующих величин: &lt;br /&gt;
#Cписок глубин посещенных вершин &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;. Глубина текущей вершины добавляется в конец списка при входе в данную вершину, а также после каждого возвращения из её сына.&lt;br /&gt;
#Список посещений узлов &amp;lt;tex&amp;gt;\mathtt{vtx}&amp;lt;/tex&amp;gt;, строящийся аналогично предыдущему, только добавляется не глубина а сама вершина.&lt;br /&gt;
#Значение функции &amp;lt;tex&amp;gt;\mathtt{I}[u]&amp;lt;/tex&amp;gt;, возвращающей индекс в списке глубин &amp;lt;tex&amp;gt;d&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;
Будем считать, что &amp;lt;tex&amp;gt;\mathrm{rmq}(d,l,r)&amp;lt;/tex&amp;gt; возвращает индекс минимального элемента в &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; на отрезке &amp;lt;tex&amp;gt;[l..r]&amp;lt;/tex&amp;gt;. Тогда ответом на запрос &amp;lt;tex&amp;gt;\mathrm{lca}(u, v)&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;\mathtt{I}[u] \leqslant \mathtt{I}[v]&amp;lt;/tex&amp;gt;, будет &amp;lt;tex&amp;gt;\mathtt{vtx}[\mathrm{rmq}(d,\mathtt{I}[u], \mathtt{I}[v])]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Доказательство корректности алгоритма ===&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement=&lt;br /&gt;
Наименьшему общему предку вершин &amp;lt;tex&amp;gt;u, v&amp;lt;/tex&amp;gt; соответствует минимальная глубина на отрезке &amp;lt;tex&amp;gt;d[\mathtt{I}[u], \mathtt{I}[v]]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим два узла &amp;lt;tex&amp;gt;u, v&amp;lt;/tex&amp;gt; корневого дерева &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. Рассмотрим отрезок &amp;lt;tex&amp;gt;d[\mathtt{I}[u]..\mathtt{I}[v]]&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;w&amp;lt;/tex&amp;gt; (в дереве есть только один простой путь между вершинами), а следовательно минимум на отрезке никак не больше глубины &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Заметим, что в момент добавления &amp;lt;tex&amp;gt;\mathtt{I}[u]&amp;lt;/tex&amp;gt; обход посещал поддерево с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. В момент добавления &amp;lt;tex&amp;gt;\mathtt{I}[v]&amp;lt;/tex&amp;gt; мы все еще в поддереве с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Значит, и на отрезке между &amp;lt;tex&amp;gt;\mathtt{I}[u]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathtt{I}[v]&amp;lt;/tex&amp;gt; мы находились внутри поддерева с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Отсюда сделаем заключение, что на рассматриваемом отрезке не посещалась вершина, отличная от &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;, с глубиной меньшей либо равной глубины &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;, т. к. подобной вершины нет в поддереве с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}.&lt;br /&gt;
&lt;br /&gt;
== Пример ==&lt;br /&gt;
Рассмотрим дерево на рисунке 1. Найдем наименьшего общего предка вершин, помеченных красным цветом.&lt;br /&gt;
Список глубин, получающийся в результате обхода в глубину {{---}} &amp;lt;tex&amp;gt;[0, 1, 2, 1, 2, 1, 0, 1, 0].&amp;lt;/tex&amp;gt;&lt;br /&gt;
Глубина наименьшего общего предка красных вершин равна минимуму на отрезке &amp;lt;tex&amp;gt;[2, 1, 0, 1].&amp;lt;/tex&amp;gt;&lt;br /&gt;
[[Файл:Lca to rmq.png(2).png|thumb|center|400x200px|Рисунок к примеру]]&lt;br /&gt;
&amp;lt;div style='clear:left;'&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Сложность ==&lt;br /&gt;
&lt;br /&gt;
Для нахождения минимального элемента на отрезке можно использовать [[Алгоритм Фарака-Колтона и Бендера|алгоритм Фарака-Колтона и Бендера]] для &amp;lt;tex&amp;gt;\pm 1RMQ&amp;lt;/tex&amp;gt;, т. к. соседние элементы в списке глубин отличаются не более чем на единицу. Длина списка глубин составляет &amp;lt;tex&amp;gt;(2n - 1)&amp;lt;/tex&amp;gt;, таким образом, препроцессинг работает за &amp;lt;tex&amp;gt;O(n)&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;
*[[Решение RMQ с помощью разреженной таблицы]]&lt;br /&gt;
*[[Алгоритм Фарака-Колтона и Бендера]]&lt;br /&gt;
*[[Сведение задачи RMQ к задаче LCA]]&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
*[http://e-maxx.ru/algo/lca Наименьший общий предок. Нахождение за O (sqrt (N)) и O (log N) с препроцессингом O (N)]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Задача о наименьшем общем предке]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Lca_to_rmq.png(2).png&amp;diff=48213</id>
		<title>Файл:Lca to rmq.png(2).png</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A4%D0%B0%D0%B9%D0%BB:Lca_to_rmq.png(2).png&amp;diff=48213"/>
				<updated>2015-06-09T07:21:02Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: загружена новая версия «Файл:Lca to rmq.png(2).png»&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8_LCA_%D0%BA_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B5_RMQ&amp;diff=48209</id>
		<title>Сведение задачи LCA к задаче RMQ</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8_LCA_%D0%BA_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B5_RMQ&amp;diff=48209"/>
				<updated>2015-06-09T06:51:09Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Препроцессинг */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Шаблон:Задача&lt;br /&gt;
|definition = &lt;br /&gt;
Пусть дано корневое дерево &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. На вход подаются запросы вида &amp;lt;tex&amp;gt;(u,\;v)&amp;lt;/tex&amp;gt;, для каждого запроса требуется найти их наименьшего общего предка.&lt;br /&gt;
}}&lt;br /&gt;
{{Определение &lt;br /&gt;
|id=lca_suf_tree&lt;br /&gt;
|definition = '''Наименьшим общим предком''' (англ. ''least common ancestor'') двух узлов &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;T&amp;lt;/tex&amp;gt; называется узел &amp;lt;tex&amp;gt;w&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;, имеет наибольшую глубину.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Алгоритм ==&lt;br /&gt;
=== Идея ===&lt;br /&gt;
Будем решать задачу &amp;lt;tex&amp;gt;LCA&amp;lt;/tex&amp;gt;, уже умея решать задачу &amp;lt;tex&amp;gt;RMQ&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;-того элементов сводится к запросу минимума на отрезке массива, который будет введен позднее.&lt;br /&gt;
&lt;br /&gt;
=== Препроцессинг ===&lt;br /&gt;
Для каждой вершины &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; определим глубину с помощью следующей рекурсивной формулы:&lt;br /&gt;
:&amp;lt;tex&amp;gt;\mathrm{depth}(u) = \begin{cases}&lt;br /&gt;
0 &amp;amp; u = \mathrm{root}(T),\\&lt;br /&gt;
\mathrm{depth}(v) + 1 &amp;amp; u = \mathrm{son}(v).&lt;br /&gt;
\end{cases}&amp;lt;/tex&amp;gt;&lt;br /&gt;
Ясно, что глубина вершины элементарным образом поддерживается во время обхода в глубину.&lt;br /&gt;
&lt;br /&gt;
Запустим [[Обход в глубину, цвета вершин|обход в глубину]] из корня, который будет вычислять значения следующих величин: &lt;br /&gt;
#Cписок глубин посещенных вершин &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;. Глубина текущей вершины добавляется в конец списка при входе в данную вершину, а также после каждого возвращения из её сына.&lt;br /&gt;
#Список посещений узлов &amp;lt;tex&amp;gt;\mathtt{vtx}&amp;lt;/tex&amp;gt;, строящийся аналогично предыдущему, только добавляется не глубина а сама вершина.&lt;br /&gt;
#Значение функции &amp;lt;tex&amp;gt;\mathtt{I}[u]&amp;lt;/tex&amp;gt;, возвращающей индекс в списке глубин &amp;lt;tex&amp;gt;d&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;
Будем считать, что &amp;lt;tex&amp;gt;\mathrm{rmq}(d,l,r)&amp;lt;/tex&amp;gt; возвращает индекс минимального элемента в &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; на отрезке &amp;lt;tex&amp;gt;[l..r]&amp;lt;/tex&amp;gt;. Тогда ответом на запрос &amp;lt;tex&amp;gt;\mathrm{lca}(u, v)&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;\mathtt{I}[u] \leqslant \mathtt{I}[v]&amp;lt;/tex&amp;gt;, будет &amp;lt;tex&amp;gt;\mathtt{vtx}[\mathrm{rmq}(d,\mathtt{I}[u], \mathtt{I}[v])]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Доказательство корректности алгоритма ===&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement=&lt;br /&gt;
Наименьшему общему предку вершин &amp;lt;tex&amp;gt;u, v&amp;lt;/tex&amp;gt; соответствует минимальная глубина на отрезке &amp;lt;tex&amp;gt;d[\mathtt{I}[u], \mathtt{I}[v]]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим два узла &amp;lt;tex&amp;gt;u, v&amp;lt;/tex&amp;gt; корневого дерева &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. Рассмотрим отрезок &amp;lt;tex&amp;gt;d[\mathtt{I}[u]..\mathtt{I}[v]]&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;w&amp;lt;/tex&amp;gt; (в дереве есть только один простой путь между вершинами), а следовательно минимум на отрезке никак не больше глубины &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Заметим, что в момент добавления &amp;lt;tex&amp;gt;\mathtt{I}[u]&amp;lt;/tex&amp;gt; обход посещал поддерево с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. В момент добавления &amp;lt;tex&amp;gt;\mathtt{I}[v]&amp;lt;/tex&amp;gt; мы все еще в поддереве с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Значит, и на отрезке между &amp;lt;tex&amp;gt;\mathtt{I}[u]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathtt{I}[v]&amp;lt;/tex&amp;gt; мы находились внутри поддерева с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Отсюда сделаем заключение, что на рассматриваемом отрезке не посещалась вершина, отличная от &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;, с глубиной меньшей либо равной глубины &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;, т. к. подобной вершины нет в поддереве с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}.&lt;br /&gt;
&lt;br /&gt;
== Пример ==&lt;br /&gt;
Рассмотрим дерево на рисунке 1. Найдем наименьшего общего предка вершин, помеченных красным цветом.&lt;br /&gt;
Список глубин, получающийся в результате обхода в глубину {{---}} &amp;lt;tex&amp;gt;[0, 1, 2, 1, 2, 1, 0, 1, 0].&amp;lt;/tex&amp;gt;&lt;br /&gt;
Глубина наименьшего общего предка красных вершин равна минимуму на отрезке &amp;lt;tex&amp;gt;[2, 1, 0, 1].&amp;lt;/tex&amp;gt;&lt;br /&gt;
[[Файл:Lca to rmq.png(2).png|thumb|center|300x150px|Рисунок к примеру]]&lt;br /&gt;
&amp;lt;div style='clear:left;'&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Сложность ==&lt;br /&gt;
&lt;br /&gt;
Для нахождения минимального элемента на отрезке можно использовать [[Алгоритм Фарака-Колтона и Бендера|алгоритм Фарака-Колтона и Бендера]] для &amp;lt;tex&amp;gt;\pm 1RMQ&amp;lt;/tex&amp;gt;, т. к. соседние элементы в списке глубин отличаются не более чем на единицу. Длина списка глубин составляет &amp;lt;tex&amp;gt;(2n - 1)&amp;lt;/tex&amp;gt;, таким образом, препроцессинг работает за &amp;lt;tex&amp;gt;O(n)&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;
*[[Решение RMQ с помощью разреженной таблицы]]&lt;br /&gt;
*[[Алгоритм Фарака-Колтона и Бендера]]&lt;br /&gt;
*[[Сведение задачи RMQ к задаче LCA]]&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
*[http://e-maxx.ru/algo/lca Наименьший общий предок. Нахождение за O (sqrt (N)) и O (log N) с препроцессингом O (N)]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Задача о наименьшем общем предке]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8_LCA_%D0%BA_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B5_RMQ&amp;diff=48208</id>
		<title>Сведение задачи LCA к задаче RMQ</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8_LCA_%D0%BA_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B5_RMQ&amp;diff=48208"/>
				<updated>2015-06-09T06:46:50Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Алгоритм */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Шаблон:Задача&lt;br /&gt;
|definition = &lt;br /&gt;
Пусть дано корневое дерево &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. На вход подаются запросы вида &amp;lt;tex&amp;gt;(u,\;v)&amp;lt;/tex&amp;gt;, для каждого запроса требуется найти их наименьшего общего предка.&lt;br /&gt;
}}&lt;br /&gt;
{{Определение &lt;br /&gt;
|id=lca_suf_tree&lt;br /&gt;
|definition = '''Наименьшим общим предком''' (англ. ''least common ancestor'') двух узлов &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;T&amp;lt;/tex&amp;gt; называется узел &amp;lt;tex&amp;gt;w&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;, имеет наибольшую глубину.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Алгоритм ==&lt;br /&gt;
=== Идея ===&lt;br /&gt;
Будем решать задачу &amp;lt;tex&amp;gt;LCA&amp;lt;/tex&amp;gt;, уже умея решать задачу &amp;lt;tex&amp;gt;RMQ&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;-того элементов сводится к запросу минимума на отрезке массива, который будет введен позднее.&lt;br /&gt;
&lt;br /&gt;
=== Препроцессинг ===&lt;br /&gt;
Для каждой вершины &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; определим глубину с помощью следующей рекурсивной формулы:&lt;br /&gt;
:&amp;lt;tex&amp;gt;\mathrm{depth}(u) = \begin{cases}&lt;br /&gt;
0 &amp;amp; u = \mathrm{root}(T),\\&lt;br /&gt;
\mathrm{depth}(v) + 1 &amp;amp; u = \mathrm{son}(v).&lt;br /&gt;
\end{cases}&amp;lt;/tex&amp;gt;&lt;br /&gt;
Ясно, что глубина вершины элементарным образом поддерживается во время обхода в глубину.&lt;br /&gt;
&lt;br /&gt;
Запустим [[Обход в глубину, цвета вершин|обход в глубину]] из корня, который будет вычислять значения следующих величин: &lt;br /&gt;
#Cписок глубин посещенных вершин &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;. Глубина текущей вершины добавляется в конец списка при входе в данную вершину, а также после каждого возвращения из её сына.&lt;br /&gt;
#Список посещений узлов &amp;lt;tex&amp;gt;\mathtt{vtx}&amp;lt;/tex&amp;gt;, строящийся аналогично предыдущему, только добавляется не глубина а сама вершина.&lt;br /&gt;
#Значение функции &amp;lt;tex&amp;gt;\mathtt{I}[u]&amp;lt;/tex&amp;gt;, возвращающей любой индекс в списке глубин &amp;lt;tex&amp;gt;d&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;
Будем считать, что &amp;lt;tex&amp;gt;\mathrm{rmq}(d,l,r)&amp;lt;/tex&amp;gt; возвращает индекс минимального элемента в &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; на отрезке &amp;lt;tex&amp;gt;[l..r]&amp;lt;/tex&amp;gt;. Тогда ответом на запрос &amp;lt;tex&amp;gt;\mathrm{lca}(u, v)&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;\mathtt{I}[u] \leqslant \mathtt{I}[v]&amp;lt;/tex&amp;gt;, будет &amp;lt;tex&amp;gt;\mathtt{vtx}[\mathrm{rmq}(d,\mathtt{I}[u], \mathtt{I}[v])]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Доказательство корректности алгоритма ===&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement=&lt;br /&gt;
Наименьшему общему предку вершин &amp;lt;tex&amp;gt;u, v&amp;lt;/tex&amp;gt; соответствует минимальная глубина на отрезке &amp;lt;tex&amp;gt;d[\mathtt{I}[u], \mathtt{I}[v]]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим два узла &amp;lt;tex&amp;gt;u, v&amp;lt;/tex&amp;gt; корневого дерева &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. Рассмотрим отрезок &amp;lt;tex&amp;gt;d[\mathtt{I}[u]..\mathtt{I}[v]]&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;w&amp;lt;/tex&amp;gt; (в дереве есть только один простой путь между вершинами), а следовательно минимум на отрезке никак не больше глубины &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Заметим, что в момент добавления &amp;lt;tex&amp;gt;\mathtt{I}[u]&amp;lt;/tex&amp;gt; обход посещал поддерево с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. В момент добавления &amp;lt;tex&amp;gt;\mathtt{I}[v]&amp;lt;/tex&amp;gt; мы все еще в поддереве с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Значит, и на отрезке между &amp;lt;tex&amp;gt;\mathtt{I}[u]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathtt{I}[v]&amp;lt;/tex&amp;gt; мы находились внутри поддерева с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Отсюда сделаем заключение, что на рассматриваемом отрезке не посещалась вершина, отличная от &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;, с глубиной меньшей либо равной глубины &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;, т. к. подобной вершины нет в поддереве с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}.&lt;br /&gt;
&lt;br /&gt;
== Пример ==&lt;br /&gt;
Рассмотрим дерево на рисунке 1. Найдем наименьшего общего предка вершин, помеченных красным цветом.&lt;br /&gt;
Список глубин, получающийся в результате обхода в глубину {{---}} &amp;lt;tex&amp;gt;[0, 1, 2, 1, 2, 1, 0, 1, 0].&amp;lt;/tex&amp;gt;&lt;br /&gt;
Глубина наименьшего общего предка красных вершин равна минимуму на отрезке &amp;lt;tex&amp;gt;[2, 1, 0, 1].&amp;lt;/tex&amp;gt;&lt;br /&gt;
[[Файл:Lca to rmq.png(2).png|thumb|center|300x150px|Рисунок к примеру]]&lt;br /&gt;
&amp;lt;div style='clear:left;'&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Сложность ==&lt;br /&gt;
&lt;br /&gt;
Для нахождения минимального элемента на отрезке можно использовать [[Алгоритм Фарака-Колтона и Бендера|алгоритм Фарака-Колтона и Бендера]] для &amp;lt;tex&amp;gt;\pm 1RMQ&amp;lt;/tex&amp;gt;, т. к. соседние элементы в списке глубин отличаются не более чем на единицу. Длина списка глубин составляет &amp;lt;tex&amp;gt;(2n - 1)&amp;lt;/tex&amp;gt;, таким образом, препроцессинг работает за &amp;lt;tex&amp;gt;O(n)&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;
*[[Решение RMQ с помощью разреженной таблицы]]&lt;br /&gt;
*[[Алгоритм Фарака-Колтона и Бендера]]&lt;br /&gt;
*[[Сведение задачи RMQ к задаче LCA]]&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
*[http://e-maxx.ru/algo/lca Наименьший общий предок. Нахождение за O (sqrt (N)) и O (log N) с препроцессингом O (N)]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Задача о наименьшем общем предке]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8_LCA_%D0%BA_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B5_RMQ&amp;diff=48207</id>
		<title>Сведение задачи LCA к задаче RMQ</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8_LCA_%D0%BA_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B5_RMQ&amp;diff=48207"/>
				<updated>2015-06-09T06:45:39Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Пример */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Шаблон:Задача&lt;br /&gt;
|definition = &lt;br /&gt;
Пусть дано корневое дерево &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. На вход подаются запросы вида &amp;lt;tex&amp;gt;(u,\;v)&amp;lt;/tex&amp;gt;, для каждого запроса требуется найти их наименьшего общего предка.&lt;br /&gt;
}}&lt;br /&gt;
{{Определение &lt;br /&gt;
|id=lca_suf_tree&lt;br /&gt;
|definition = '''Наименьшим общим предком''' (англ. ''least common ancestor'') двух узлов &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;T&amp;lt;/tex&amp;gt; называется узел &amp;lt;tex&amp;gt;w&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;, имеет наибольшую глубину.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Алгоритм ==&lt;br /&gt;
=== Идея ===&lt;br /&gt;
Будем решать задачу LCA, уже умея решать задачу RMQ. Тогда поиск наименьшего общего предка &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-того и &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-того элементов сводится к запросу минимума на отрезке массива, который будет введен позднее.&lt;br /&gt;
&lt;br /&gt;
=== Препроцессинг ===&lt;br /&gt;
Для каждой вершины &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; определим глубину с помощью следующей рекурсивной формулы:&lt;br /&gt;
:&amp;lt;tex&amp;gt;\mathrm{depth}(u) = \begin{cases}&lt;br /&gt;
0 &amp;amp; u = \mathrm{root}(T),\\&lt;br /&gt;
\mathrm{depth}(v) + 1 &amp;amp; u = \mathrm{son}(v).&lt;br /&gt;
\end{cases}&amp;lt;/tex&amp;gt;&lt;br /&gt;
Ясно, что глубина вершины элементарным образом поддерживается во время обхода в глубину.&lt;br /&gt;
&lt;br /&gt;
Запустим [[Обход в глубину, цвета вершин|обход в глубину]] из корня, который будет вычислять значения следующих величин: &lt;br /&gt;
#Cписок глубин посещенных вершин &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;. Глубина текущей вершины добавляется в конец списка при входе в данную вершину, а также после каждого возвращения из её сына.&lt;br /&gt;
#Список посещений узлов &amp;lt;tex&amp;gt;\mathtt{vtx}&amp;lt;/tex&amp;gt;, строящийся аналогично предыдущему, только добавляется не глубина а сама вершина.&lt;br /&gt;
#Значение функции &amp;lt;tex&amp;gt;\mathtt{I}[u]&amp;lt;/tex&amp;gt;, возвращающей любой индекс в списке глубин &amp;lt;tex&amp;gt;d&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;
Будем считать, что &amp;lt;tex&amp;gt;\mathrm{rmq}(d,l,r)&amp;lt;/tex&amp;gt; возвращает индекс минимального элемента в &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; на отрезке &amp;lt;tex&amp;gt;[l..r]&amp;lt;/tex&amp;gt;. Тогда ответом на запрос &amp;lt;tex&amp;gt;\mathrm{lca}(u, v)&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;\mathtt{I}[u] \leqslant \mathtt{I}[v]&amp;lt;/tex&amp;gt;, будет &amp;lt;tex&amp;gt;\mathtt{vtx}[\mathrm{rmq}(d,\mathtt{I}[u], \mathtt{I}[v])]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Доказательство корректности алгоритма ===&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement=&lt;br /&gt;
Наименьшему общему предку вершин &amp;lt;tex&amp;gt;u, v&amp;lt;/tex&amp;gt; соответствует минимальная глубина на отрезке &amp;lt;tex&amp;gt;d[\mathtt{I}[u], \mathtt{I}[v]]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим два узла &amp;lt;tex&amp;gt;u, v&amp;lt;/tex&amp;gt; корневого дерева &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. Рассмотрим отрезок &amp;lt;tex&amp;gt;d[\mathtt{I}[u]..\mathtt{I}[v]]&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;w&amp;lt;/tex&amp;gt; (в дереве есть только один простой путь между вершинами), а следовательно минимум на отрезке никак не больше глубины &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Заметим, что в момент добавления &amp;lt;tex&amp;gt;\mathtt{I}[u]&amp;lt;/tex&amp;gt; обход посещал поддерево с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. В момент добавления &amp;lt;tex&amp;gt;\mathtt{I}[v]&amp;lt;/tex&amp;gt; мы все еще в поддереве с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Значит, и на отрезке между &amp;lt;tex&amp;gt;\mathtt{I}[u]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathtt{I}[v]&amp;lt;/tex&amp;gt; мы находились внутри поддерева с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Отсюда сделаем заключение, что на рассматриваемом отрезке не посещалась вершина, отличная от &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;, с глубиной меньшей либо равной глубины &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;, т. к. подобной вершины нет в поддереве с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}.&lt;br /&gt;
&lt;br /&gt;
== Пример ==&lt;br /&gt;
Рассмотрим дерево на рисунке 1. Найдем наименьшего общего предка вершин, помеченных красным цветом.&lt;br /&gt;
Список глубин, получающийся в результате обхода в глубину {{---}} &amp;lt;tex&amp;gt;[0, 1, 2, 1, 2, 1, 0, 1, 0].&amp;lt;/tex&amp;gt;&lt;br /&gt;
Глубина наименьшего общего предка красных вершин равна минимуму на отрезке &amp;lt;tex&amp;gt;[2, 1, 0, 1].&amp;lt;/tex&amp;gt;&lt;br /&gt;
[[Файл:Lca to rmq.png(2).png|thumb|center|300x150px|Рисунок к примеру]]&lt;br /&gt;
&amp;lt;div style='clear:left;'&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Сложность ==&lt;br /&gt;
&lt;br /&gt;
Для нахождения минимального элемента на отрезке можно использовать [[Алгоритм Фарака-Колтона и Бендера|алгоритм Фарака-Колтона и Бендера]] для &amp;lt;tex&amp;gt;\pm 1RMQ&amp;lt;/tex&amp;gt;, т. к. соседние элементы в списке глубин отличаются не более чем на единицу. Длина списка глубин составляет &amp;lt;tex&amp;gt;(2n - 1)&amp;lt;/tex&amp;gt;, таким образом, препроцессинг работает за &amp;lt;tex&amp;gt;O(n)&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;
*[[Решение RMQ с помощью разреженной таблицы]]&lt;br /&gt;
*[[Алгоритм Фарака-Колтона и Бендера]]&lt;br /&gt;
*[[Сведение задачи RMQ к задаче LCA]]&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
*[http://e-maxx.ru/algo/lca Наименьший общий предок. Нахождение за O (sqrt (N)) и O (log N) с препроцессингом O (N)]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Задача о наименьшем общем предке]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=48206</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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=48206"/>
				<updated>2015-06-09T06:44:06Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Обобщение алгоритма */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Алгоритм Карккайнена-Сандерса''' (Kärkkäinen, Sanders) — алгоритм построения [[суффиксный массив | суффиксного массива]] за линейное время.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Используемые обозначения ==&lt;br /&gt;
* В данном конспекте используется 0-индексация.&lt;br /&gt;
* &amp;lt;tex&amp;gt;S[i..j] &amp;lt;/tex&amp;gt; — подстрока строки &amp;lt;tex&amp;gt; S &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;-й символы включительно.&lt;br /&gt;
* Пусть длина строки &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;. Обозначим &amp;lt;tex&amp;gt; S^*[i..j] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; j \geqslant n &amp;lt;/tex&amp;gt; как строку &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt;, дополненную защитными символами &amp;lt;tex&amp;gt; \$ &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;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Четным суффиксом''' назовем суффикс, начинающийся в четной позиции. &amp;lt;br&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;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left( \dfrac{n}{2}\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для упрощения алгоритма вначале дополним нашу строку до четной длины (например, добавлением &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; в конец). На шаге слияния мы сможем избавиться от него.&lt;br /&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;
&lt;br /&gt;
# Отобразим исходную строку &amp;lt;tex&amp;gt; S^* &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; длины  &amp;lt;tex&amp;gt; \dfrac{n}{2} &amp;lt;/tex&amp;gt;  следующим образом:&lt;br /&gt;
#* Сделаем список, состоящий из пар символов вида &amp;lt;tex&amp;gt; S^*[i..i + 1] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; i \bmod 2 = 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его цифровой сортировкой за линейное время и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S^*[1..n] &amp;lt;/tex&amp;gt; в алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;, получив строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; половинной длины.&lt;br /&gt;
# Рекурсивно построим суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Построим суффиксный массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;. Очевидно, &amp;lt;tex&amp;gt; A_{S_o}[i] = 2 A_{S'}[i] + 1 &amp;lt;/tex&amp;gt;, так отношение упорядоченности любых двух строк в старом алфавите &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt; эквивалентно отношению упорядоченности в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; по его построению.&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для четных суффиксов=====&lt;br /&gt;
На этом шаге мы за линейное время получим суффиксный массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; для четных суффиксов, используя уже построенный &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что сортировка множества четных суффиксов &amp;lt;tex&amp;gt; \{ S^*[i..n] \mid i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке множества пар &amp;lt;tex&amp;gt; \{ (S^*[i], S^*[i+1..n]) \mid i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt;. Однако &amp;lt;tex&amp;gt; S^*[i+1..n] &amp;lt;/tex&amp;gt; — нечетный суффикс, и его относительную позицию мы уже узнали на шаге 1.&lt;br /&gt;
&lt;br /&gt;
Таким образом, чтобы отсортировать эти пары за линейное время, сначала сразу выпишем их в порядке возрастания второго элемента пары (то есть в порядке вхождения в массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;), а потом отсортируем устойчивой сортировкой подсчетом по первым элементам. После этого легко можно восстановить массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n / 2 - 1&lt;br /&gt;
     M.add(Pair(S[&amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 quick_stable_sort(M)&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
    &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&gt;
&lt;br /&gt;
Заметим, что массив &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; перед сортировкой подсчетом не был явно отсортирован по вторым элементам, и хранил не суффиксы, а их позиции в строке &amp;lt;tex&amp;gt; S &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;
Для суффиксного дерева третий шаг алгоритма опирается на специфические особенности суффиксных деревьев, которые не присущи суффиксным массивам&amp;lt;ref&amp;gt;M. Farach. Optimal suffix tree construction with large alphabets. http://www.cs.rutgers.edu/~farach/pubs/FarFerrMuthu00.pdf &amp;lt;/ref&amp;gt; .  &lt;br /&gt;
В случае [[Суффиксный массив|суффиксного массива]] слияние становится очень сложным, но все же оно было реализовано в алгоритме Ким-Сим-Парк-Парка&amp;lt;ref&amp;gt; D. K. Kim, J. S. Sim, H. Park, and K. Park. Linear-time construction of suffix arrays. http://www.springerlink.com/content/568156021q45r320/&amp;lt;/ref&amp;gt;. Однако простой модификацией алгоритма можно значительно упростить его.&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
Покажем первые два шага агоритма для строки '''ababbbaa'''.&lt;br /&gt;
&lt;br /&gt;
Во-первых, добавив защитный символ '''$''', получив строку '''ababbbaa$''' (для этого алгоритма он не требуется, но может понадобиться в применениях суффиксного массива). Во-вторых, дополним ее до четной длины, получив '''ababbbaa$$'''.&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для нечетных суффиксов=====&lt;br /&gt;
# В новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет четыре элемента — '''ba''', '''bb''', '''a$''', '''$$'''. После сортировки они получат номера 2, 3, 1 и 0 соответственно.&lt;br /&gt;
# Переводим строку &amp;lt;tex&amp;gt;S^*[1..n]&amp;lt;/tex&amp;gt; = '''babbbaa$$$''' в новый алфавит. Сжатой строкой &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; будет '''23210'''.&lt;br /&gt;
# После рекурсивного вызова получим, что &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [4, 3, 2, 0, 1], и &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; = [9, 7, 5, 1, 3].&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для четных суффиксов=====&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [(&amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt;, 9), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 7), (&amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, 5), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 1), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 3)].&lt;br /&gt;
# После сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt;= [(&amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt;, 9), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 7), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 1), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 3), (&amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, 5)].&lt;br /&gt;
# Восстановив массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;, получаем [8, 6, 0, 2, 4], что действительно является суффиксным массивом для четных суффиксов.&lt;br /&gt;
&lt;br /&gt;
=====Слияние суффиксных массивов=====&lt;br /&gt;
Если бы мы умели сливать &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; за линейное время, получили бы:&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 9 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; a\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; aa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ababbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; baa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; babbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Как и было сказано вначале, избавиться от лишних '''$''' легко, так как суффиксы, им соответствующие, будут первыми в суффиксном массиве (в данном случае достаточно выбросить &amp;quot;9&amp;quot; из суффиксного массива).&lt;br /&gt;
&lt;br /&gt;
== Алгоритм Карккайнена-Сандерса == &lt;br /&gt;
Изменим изначальный алгоритм следующим образом:&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих позициям, не кратным трем. Рекурсивно сведем это к построению суффиксного массива для строки длиной в две трети исходной.&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих кратным трем позициям, используя результат первого шага, за линейное время.&lt;br /&gt;
# Сольем эти суффиксные массивы в один за линейное время.&lt;br /&gt;
&lt;br /&gt;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left(\dfrac23 n\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого также является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt; (это видно из того, что сумма геометрической прогрессии с основанием &amp;lt;tex&amp;gt; \dfrac23 &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;).&lt;br /&gt;
&lt;br /&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;S^*&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* '''База рекурсии'''&lt;br /&gt;
Для этого алгоритма минимальной базой рекурсии будет строка длиной 4, так как она дополняется до длины 6, после чего вновь следует рекурсивный вызов строки длиной 4, и, если бы база была меньше 4, алгоритм вошел бы в бесконечную рекурсию. На этом этапе также можно применить обычную сортировку суффиксов, так как это потребует &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
На этом шаге строится суффиксный массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; для множества суффиксов &amp;lt;tex&amp;gt; \{ S^*[i..n-1] \mid i \bmod 3 \ne 0 \} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Получим строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; аналогично предыдущему алгоритму:&lt;br /&gt;
#* Сделаем список, состоящий из троек &amp;lt;tex&amp;gt; S^*[i..i+2]&amp;lt;/tex&amp;gt; , где &amp;lt;tex&amp;gt; i \bmod 3 \ne 0 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его за линейное время цифровой сортировкой и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S^*[1..n]S^*[2..n+1] &amp;lt;/tex&amp;gt; в строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; длиной &amp;lt;tex&amp;gt; \dfrac23 n &amp;lt;/tex&amp;gt; в алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;. Тогда суффиксу &amp;lt;tex&amp;gt; S^*[i..n-1] &amp;lt;/tex&amp;gt; в старом алфавите, где &amp;lt;tex&amp;gt; i \bmod 3 = 1 &amp;lt;/tex&amp;gt;, в новом алфавите будет соответствовать строка &amp;lt;tex&amp;gt; S'\left[\dfrac{i-1}{3}..\dfrac{n}{3} - 1\right] &amp;lt;/tex&amp;gt;, а если &amp;lt;tex&amp;gt; i\bmod 3 = 2 &amp;lt;/tex&amp;gt;, то строка &amp;lt;tex&amp;gt; S'\left[\dfrac{n}{3} + \dfrac{i-2}{3}..\dfrac{2n}{3} - 1\right] &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Вызовем алгоритм рекурсивно для строки &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt;, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Пройдем по массиву &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt; A_{S'}[i] &amp;lt; \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j = 3A_{S'}[i] + 1 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;, если же &amp;lt;tex&amp;gt; A_{S'}[i] \geqslant \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j =  3\left(A_{S'}[i] - \dfrac{n}{3}\right) + 2 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S^* &amp;lt;/tex&amp;gt;. Псевдокод получения &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;.length - 1:&lt;br /&gt;
    '''if''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] &amp;lt; n/3:&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] + 1)&lt;br /&gt;
    '''else''':&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * (&amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] - n/3) + 2)&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
Этот шаг также аналогичен первой версии алгоритма. Сортировка множества &amp;lt;tex&amp;gt; \{ S^*[i..n-1] \mid i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке пар &amp;lt;tex&amp;gt; \{ (S^*[i], S^*[i+1..n-1]) \mid i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; S^*[i+1..n-1] &amp;lt;/tex&amp;gt; — суффиксы в позициях, равных 1 по модулю 3, относительный порядок которых уже известен. Выпишем эти пары в порядке вхождения их в &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и отсортируем по первому элементу устойчивой сортировкой подсчетом, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;. Псевдокод этого шага:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' 2n/3 - 1:&lt;br /&gt;
     '''if''' &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[i] % 3 == 1:&lt;br /&gt;
         M.add(Pair(S*[&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 '''for''' i = 0 '''to''' n/3 - 1:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&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; A_{S_0} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, чтобы получить суффиксный массив &amp;lt;tex&amp;gt; A_{S} &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;
&lt;br /&gt;
Пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 1 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Это аналогично сравнению пар &amp;lt;tex&amp;gt; (S^*[i], S^*[i+1..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S^*[j], S^*[j+1..n-1]) &amp;lt;/tex&amp;gt;. Сравнить первые элементы пар мы можем за &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt;, а относительный порядок вторых элементов пар нам уже известен, так как они соответствуют позициям, равным 2 и 1 по модулю 3 соответственно.&lt;br /&gt;
&lt;br /&gt;
Аналогично, пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 2 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Тогда это аналогично сравнению троек &amp;lt;tex&amp;gt; (S^*[i], S^*[i+1], S^*[i+2..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S^*[j], S^*[j+1], S^*[j+2..n-1]) &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;A_{S}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 &amp;lt;font color=green&amp;gt;// Вначале предподсчитаем за O(n) обратную перестановку для суффиксного массива &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;, то есть массив rank такой, что &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[rank[i]] = i.&lt;br /&gt;
 // Тогда мы сможем за O(1) сравнивать суффиксы по их позиции. &amp;lt;/font&amp;gt;&lt;br /&gt;
 rank = inverse(&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;) &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3 '''and''' j &amp;lt; n/3:&lt;br /&gt;
     pos12 = &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i]&lt;br /&gt;
     pos0  = &amp;lt;tex&amp;gt; A_{0} &amp;lt;/tex&amp;gt;[j]&lt;br /&gt;
     '''if''' pos12 % 3 == 1:&lt;br /&gt;
        ''' if''' Pair(S*[pos12], rank[pos12 + 1]) &amp;lt; Pair(S*[pos0], rank[pos0 + 1]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++  &lt;br /&gt;
     '''else''':&lt;br /&gt;
         '''if''' Triple(S*[pos12], S*[pos12 + 1], rank[pos12 + 2]) &amp;lt; Triple(S*[pos0], S*[pos0 + 1], rank[pos0 + 2]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++ &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i])&lt;br /&gt;
     i++&lt;br /&gt;
 '''while''' j &amp;lt; n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{0}} &amp;lt;/tex&amp;gt;[j])&lt;br /&gt;
     j++&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;
Построим суффиксный массив для строки '''abbacab'''. После добавления защитного символа и дополнения до кратной трем длины, получим '''abbacab$$'''.&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
# Тройками, соответствующими равными 1 по модулю 3 позициям, будут: '''bba''', '''cab''', '''$$$''', соответствующими равным 2 по модулю 3 — '''bac''', '''cab$''', '''$$$'''. Новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет содержать элементы '''bba''', '''cab''', '''$$$''', '''bac''', '''ab$''', которые после сортировки получат номера 3, 4, 0, 2, 1 соответственно.&lt;br /&gt;
# Строкой '''bbacab$$$bacab$$$$''' в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; = 340210. &lt;br /&gt;
# После рекурсивного вызова получим &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [5, 2, 4, 3, 0, 1]. Пересчитав &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, получим [(5 - 3)*3 + 2, 2 * 3 + 1, (4 - 3) * 3 + 2, (3 - 3) * 3 + 2, 0 * 3 + 1, 1 * 3 + 1] = [8, 7, 5, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и выбрав в нем элементы, равные 1 по модулю 3, получим массив пар &amp;lt;tex&amp;gt;M&amp;lt;/tex&amp;gt; = [(&amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, 7), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 1), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 4)]&lt;br /&gt;
# После устойчивой сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''a''', 1), ('''a''', 4), ('''b''', 7)]&lt;br /&gt;
# Восстановив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;, получаем [0, 3, 6].&lt;br /&gt;
* '''Слияние суффиксных массивов'''&lt;br /&gt;
Рассмотрим, к примеру, третью итерацию слияния, к этой итерации массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 2, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0, на ней мы сливаем суффиксы, соответствующие позициям 5 и 0. &lt;br /&gt;
# Образуем тройки &amp;lt;tex&amp;gt;(S^*[5], S^*[6], S^*[7..8])&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;(S^*[0], S^*[1], S^*[2..8])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
# После получения относительного порядка суффиксов, получим тройки ('''a''', '''b''', 1) и ('''a''', '''b''', 3). Первая тройка меньше второй, поэтому добавляем суффикс, соответствующий позиции 5 в массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# В конце итерации получаем &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 3, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0.&lt;br /&gt;
К концу слияния получим &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5, 0, 3, 6, 2, 1, 4]. Так как мы добавляли один символ &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; в начале алгоритма для дополнения строки до длины, кратной трем, выбросим последний суффикс из &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;, получим в итоге, что &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [7, 5, 0, 3, 6, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; acab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; b\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; cab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
Заметим, что № 9 будет выброшен, так как в начале алгоритма был добавлен один &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt; к строке&lt;br /&gt;
 &lt;br /&gt;
{|&lt;br /&gt;
| [[Файл:Kark_sanders_stage1.png|325px|thumb| Фаза 1]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage2.png|325px|thumb| Фаза 2]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage3.png|325px|thumb| Фаза 3]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Обобщение алгоритма ==&lt;br /&gt;
Массив LCP можно получить за линейное время [[Алгоритм_Касаи_и_др. | алгоритмом Касаи]].&lt;br /&gt;
&lt;br /&gt;
На самом деле, алгоритм можно обобщить&amp;lt;ref name=&amp;quot;generalisation&amp;quot;&amp;gt; Juha Kärkkäinen, Peter Sanders and Stefan Burkhardt. Linear work suffix array construction. http://www.cs.helsinki.fi/juha.karkkainen/publications/jacm05-revised.pdf &amp;lt;/ref&amp;gt;, взяв на первом шаге, к примеру, суффиксы, позиции которых по модулю 7 дают 3, 5 и 6. Для этого потребуются некоторое усложнение алгоритма, например, сортировка оставшихся суффиксов в нескольких группах на шаге 2 и слияние нескольких групп на шаге 3, но основная идея алгоритма остается той же. Множества, которые можно выбрать, на первом шаге определяются '''разностным покрытием''' (''difference cover'').&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Разностное покрытие''' (англ. ''difference cover'') &amp;lt;tex&amp;gt; D &amp;lt;/tex&amp;gt; по модулю &amp;lt;tex&amp;gt;m &amp;lt;/tex&amp;gt; — множество чисел от &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;m - 1 &amp;lt;/tex&amp;gt; таких, что &amp;lt;tex&amp;gt; \forall i \in [0, m-1]: \exists j, k \in D: i \equiv k - j \pmod m &amp;lt;/tex&amp;gt;.&lt;br /&gt;
}} &lt;br /&gt;
Например, &amp;lt;tex&amp;gt; \{1, 2\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt; \{3, 5, 6\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt; \{1\} &amp;lt;/tex&amp;gt; — не является разностным покрытием по модулю &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;, поэтому этот алгоритм не применим к нему. Подробнее узнать, как вычислять разностное покрытие для заданного модуля можно также здесь&amp;lt;ref name=&amp;quot;generalisation&amp;quot;/&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Источники информации==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
* [http://www.cs.helsinki.fi/juha.karkkainen/publications/icalp03.pdf Juha Kärkkäinen and Peter Sanders. Simple linear work suffix array construction]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Суффиксный массив]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=48205</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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=48205"/>
				<updated>2015-06-09T06:42:35Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Алгоритм Карккайнена-Сандерса */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Алгоритм Карккайнена-Сандерса''' (Kärkkäinen, Sanders) — алгоритм построения [[суффиксный массив | суффиксного массива]] за линейное время.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Используемые обозначения ==&lt;br /&gt;
* В данном конспекте используется 0-индексация.&lt;br /&gt;
* &amp;lt;tex&amp;gt;S[i..j] &amp;lt;/tex&amp;gt; — подстрока строки &amp;lt;tex&amp;gt; S &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;-й символы включительно.&lt;br /&gt;
* Пусть длина строки &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;. Обозначим &amp;lt;tex&amp;gt; S^*[i..j] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; j \geqslant n &amp;lt;/tex&amp;gt; как строку &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt;, дополненную защитными символами &amp;lt;tex&amp;gt; \$ &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;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Четным суффиксом''' назовем суффикс, начинающийся в четной позиции. &amp;lt;br&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;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left( \dfrac{n}{2}\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для упрощения алгоритма вначале дополним нашу строку до четной длины (например, добавлением &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; в конец). На шаге слияния мы сможем избавиться от него.&lt;br /&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;
&lt;br /&gt;
# Отобразим исходную строку &amp;lt;tex&amp;gt; S^* &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; длины  &amp;lt;tex&amp;gt; \dfrac{n}{2} &amp;lt;/tex&amp;gt;  следующим образом:&lt;br /&gt;
#* Сделаем список, состоящий из пар символов вида &amp;lt;tex&amp;gt; S^*[i..i + 1] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; i \bmod 2 = 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его цифровой сортировкой за линейное время и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S^*[1..n] &amp;lt;/tex&amp;gt; в алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;, получив строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; половинной длины.&lt;br /&gt;
# Рекурсивно построим суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Построим суффиксный массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;. Очевидно, &amp;lt;tex&amp;gt; A_{S_o}[i] = 2 A_{S'}[i] + 1 &amp;lt;/tex&amp;gt;, так отношение упорядоченности любых двух строк в старом алфавите &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt; эквивалентно отношению упорядоченности в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; по его построению.&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для четных суффиксов=====&lt;br /&gt;
На этом шаге мы за линейное время получим суффиксный массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; для четных суффиксов, используя уже построенный &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что сортировка множества четных суффиксов &amp;lt;tex&amp;gt; \{ S^*[i..n] \mid i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке множества пар &amp;lt;tex&amp;gt; \{ (S^*[i], S^*[i+1..n]) \mid i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt;. Однако &amp;lt;tex&amp;gt; S^*[i+1..n] &amp;lt;/tex&amp;gt; — нечетный суффикс, и его относительную позицию мы уже узнали на шаге 1.&lt;br /&gt;
&lt;br /&gt;
Таким образом, чтобы отсортировать эти пары за линейное время, сначала сразу выпишем их в порядке возрастания второго элемента пары (то есть в порядке вхождения в массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;), а потом отсортируем устойчивой сортировкой подсчетом по первым элементам. После этого легко можно восстановить массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n / 2 - 1&lt;br /&gt;
     M.add(Pair(S[&amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 quick_stable_sort(M)&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
    &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&gt;
&lt;br /&gt;
Заметим, что массив &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; перед сортировкой подсчетом не был явно отсортирован по вторым элементам, и хранил не суффиксы, а их позиции в строке &amp;lt;tex&amp;gt; S &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;
Для суффиксного дерева третий шаг алгоритма опирается на специфические особенности суффиксных деревьев, которые не присущи суффиксным массивам&amp;lt;ref&amp;gt;M. Farach. Optimal suffix tree construction with large alphabets. http://www.cs.rutgers.edu/~farach/pubs/FarFerrMuthu00.pdf &amp;lt;/ref&amp;gt; .  &lt;br /&gt;
В случае [[Суффиксный массив|суффиксного массива]] слияние становится очень сложным, но все же оно было реализовано в алгоритме Ким-Сим-Парк-Парка&amp;lt;ref&amp;gt; D. K. Kim, J. S. Sim, H. Park, and K. Park. Linear-time construction of suffix arrays. http://www.springerlink.com/content/568156021q45r320/&amp;lt;/ref&amp;gt;. Однако простой модификацией алгоритма можно значительно упростить его.&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
Покажем первые два шага агоритма для строки '''ababbbaa'''.&lt;br /&gt;
&lt;br /&gt;
Во-первых, добавив защитный символ '''$''', получив строку '''ababbbaa$''' (для этого алгоритма он не требуется, но может понадобиться в применениях суффиксного массива). Во-вторых, дополним ее до четной длины, получив '''ababbbaa$$'''.&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для нечетных суффиксов=====&lt;br /&gt;
# В новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет четыре элемента — '''ba''', '''bb''', '''a$''', '''$$'''. После сортировки они получат номера 2, 3, 1 и 0 соответственно.&lt;br /&gt;
# Переводим строку &amp;lt;tex&amp;gt;S^*[1..n]&amp;lt;/tex&amp;gt; = '''babbbaa$$$''' в новый алфавит. Сжатой строкой &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; будет '''23210'''.&lt;br /&gt;
# После рекурсивного вызова получим, что &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [4, 3, 2, 0, 1], и &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; = [9, 7, 5, 1, 3].&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для четных суффиксов=====&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [(&amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt;, 9), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 7), (&amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, 5), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 1), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 3)].&lt;br /&gt;
# После сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt;= [(&amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt;, 9), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 7), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 1), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 3), (&amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, 5)].&lt;br /&gt;
# Восстановив массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;, получаем [8, 6, 0, 2, 4], что действительно является суффиксным массивом для четных суффиксов.&lt;br /&gt;
&lt;br /&gt;
=====Слияние суффиксных массивов=====&lt;br /&gt;
Если бы мы умели сливать &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; за линейное время, получили бы:&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 9 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; a\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; aa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ababbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; baa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; babbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Как и было сказано вначале, избавиться от лишних '''$''' легко, так как суффиксы, им соответствующие, будут первыми в суффиксном массиве (в данном случае достаточно выбросить &amp;quot;9&amp;quot; из суффиксного массива).&lt;br /&gt;
&lt;br /&gt;
== Алгоритм Карккайнена-Сандерса == &lt;br /&gt;
Изменим изначальный алгоритм следующим образом:&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих позициям, не кратным трем. Рекурсивно сведем это к построению суффиксного массива для строки длиной в две трети исходной.&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих кратным трем позициям, используя результат первого шага, за линейное время.&lt;br /&gt;
# Сольем эти суффиксные массивы в один за линейное время.&lt;br /&gt;
&lt;br /&gt;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left(\dfrac23 n\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого также является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt; (это видно из того, что сумма геометрической прогрессии с основанием &amp;lt;tex&amp;gt; \dfrac23 &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;).&lt;br /&gt;
&lt;br /&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;S^*&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* '''База рекурсии'''&lt;br /&gt;
Для этого алгоритма минимальной базой рекурсии будет строка длиной 4, так как она дополняется до длины 6, после чего вновь следует рекурсивный вызов строки длиной 4, и, если бы база была меньше 4, алгоритм вошел бы в бесконечную рекурсию. На этом этапе также можно применить обычную сортировку суффиксов, так как это потребует &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
На этом шаге строится суффиксный массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; для множества суффиксов &amp;lt;tex&amp;gt; \{ S^*[i..n-1] \mid i \bmod 3 \ne 0 \} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Получим строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; аналогично предыдущему алгоритму:&lt;br /&gt;
#* Сделаем список, состоящий из троек &amp;lt;tex&amp;gt; S^*[i..i+2]&amp;lt;/tex&amp;gt; , где &amp;lt;tex&amp;gt; i \bmod 3 \ne 0 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его за линейное время цифровой сортировкой и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S^*[1..n]S^*[2..n+1] &amp;lt;/tex&amp;gt; в строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; длиной &amp;lt;tex&amp;gt; \dfrac23 n &amp;lt;/tex&amp;gt; в алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;. Тогда суффиксу &amp;lt;tex&amp;gt; S^*[i..n-1] &amp;lt;/tex&amp;gt; в старом алфавите, где &amp;lt;tex&amp;gt; i \bmod 3 = 1 &amp;lt;/tex&amp;gt;, в новом алфавите будет соответствовать строка &amp;lt;tex&amp;gt; S'\left[\dfrac{i-1}{3}..\dfrac{n}{3} - 1\right] &amp;lt;/tex&amp;gt;, а если &amp;lt;tex&amp;gt; i\bmod 3 = 2 &amp;lt;/tex&amp;gt;, то строка &amp;lt;tex&amp;gt; S'\left[\dfrac{n}{3} + \dfrac{i-2}{3}..\dfrac{2n}{3} - 1\right] &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Вызовем алгоритм рекурсивно для строки &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt;, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Пройдем по массиву &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt; A_{S'}[i] &amp;lt; \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j = 3A_{S'}[i] + 1 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;, если же &amp;lt;tex&amp;gt; A_{S'}[i] \geqslant \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j =  3\left(A_{S'}[i] - \dfrac{n}{3}\right) + 2 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S^* &amp;lt;/tex&amp;gt;. Псевдокод получения &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;.length - 1:&lt;br /&gt;
    '''if''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] &amp;lt; n/3:&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] + 1)&lt;br /&gt;
    '''else''':&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * (&amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] - n/3) + 2)&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
Этот шаг также аналогичен первой версии алгоритма. Сортировка множества &amp;lt;tex&amp;gt; \{ S^*[i..n-1] \mid i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке пар &amp;lt;tex&amp;gt; \{ (S^*[i], S^*[i+1..n-1]) \mid i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; S^*[i+1..n-1] &amp;lt;/tex&amp;gt; — суффиксы в позициях, равных 1 по модулю 3, относительный порядок которых уже известен. Выпишем эти пары в порядке вхождения их в &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и отсортируем по первому элементу устойчивой сортировкой подсчетом, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;. Псевдокод этого шага:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' 2n/3 - 1:&lt;br /&gt;
     '''if''' &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[i] % 3 == 1:&lt;br /&gt;
         M.add(Pair(S*[&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 '''for''' i = 0 '''to''' n/3 - 1:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&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; A_{S_0} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, чтобы получить суффиксный массив &amp;lt;tex&amp;gt; A_{S} &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;
&lt;br /&gt;
Пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 1 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Это аналогично сравнению пар &amp;lt;tex&amp;gt; (S^*[i], S^*[i+1..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S^*[j], S^*[j+1..n-1]) &amp;lt;/tex&amp;gt;. Сравнить первые элементы пар мы можем за &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt;, а относительный порядок вторых элементов пар нам уже известен, так как они соответствуют позициям, равным 2 и 1 по модулю 3 соответственно.&lt;br /&gt;
&lt;br /&gt;
Аналогично, пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 2 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Тогда это аналогично сравнению троек &amp;lt;tex&amp;gt; (S^*[i], S^*[i+1], S^*[i+2..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S^*[j], S^*[j+1], S^*[j+2..n-1]) &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;A_{S}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 &amp;lt;font color=green&amp;gt;// Вначале предподсчитаем за O(n) обратную перестановку для суффиксного массива &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;, то есть массив rank такой, что &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[rank[i]] = i.&lt;br /&gt;
 // Тогда мы сможем за O(1) сравнивать суффиксы по их позиции. &amp;lt;/font&amp;gt;&lt;br /&gt;
 rank = inverse(&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;) &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3 '''and''' j &amp;lt; n/3:&lt;br /&gt;
     pos12 = &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i]&lt;br /&gt;
     pos0  = &amp;lt;tex&amp;gt; A_{0} &amp;lt;/tex&amp;gt;[j]&lt;br /&gt;
     '''if''' pos12 % 3 == 1:&lt;br /&gt;
        ''' if''' Pair(S*[pos12], rank[pos12 + 1]) &amp;lt; Pair(S*[pos0], rank[pos0 + 1]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++  &lt;br /&gt;
     '''else''':&lt;br /&gt;
         '''if''' Triple(S*[pos12], S*[pos12 + 1], rank[pos12 + 2]) &amp;lt; Triple(S*[pos0], S*[pos0 + 1], rank[pos0 + 2]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++ &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i])&lt;br /&gt;
     i++&lt;br /&gt;
 '''while''' j &amp;lt; n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{0}} &amp;lt;/tex&amp;gt;[j])&lt;br /&gt;
     j++&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;
Построим суффиксный массив для строки '''abbacab'''. После добавления защитного символа и дополнения до кратной трем длины, получим '''abbacab$$'''.&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
# Тройками, соответствующими равными 1 по модулю 3 позициям, будут: '''bba''', '''cab''', '''$$$''', соответствующими равным 2 по модулю 3 — '''bac''', '''cab$''', '''$$$'''. Новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет содержать элементы '''bba''', '''cab''', '''$$$''', '''bac''', '''ab$''', которые после сортировки получат номера 3, 4, 0, 2, 1 соответственно.&lt;br /&gt;
# Строкой '''bbacab$$$bacab$$$$''' в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; = 340210. &lt;br /&gt;
# После рекурсивного вызова получим &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [5, 2, 4, 3, 0, 1]. Пересчитав &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, получим [(5 - 3)*3 + 2, 2 * 3 + 1, (4 - 3) * 3 + 2, (3 - 3) * 3 + 2, 0 * 3 + 1, 1 * 3 + 1] = [8, 7, 5, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и выбрав в нем элементы, равные 1 по модулю 3, получим массив пар &amp;lt;tex&amp;gt;M&amp;lt;/tex&amp;gt; = [(&amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, 7), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 1), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 4)]&lt;br /&gt;
# После устойчивой сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''a''', 1), ('''a''', 4), ('''b''', 7)]&lt;br /&gt;
# Восстановив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;, получаем [0, 3, 6].&lt;br /&gt;
* '''Слияние суффиксных массивов'''&lt;br /&gt;
Рассмотрим, к примеру, третью итерацию слияния, к этой итерации массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 2, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0, на ней мы сливаем суффиксы, соответствующие позициям 5 и 0. &lt;br /&gt;
# Образуем тройки &amp;lt;tex&amp;gt;(S^*[5], S^*[6], S^*[7..8])&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;(S^*[0], S^*[1], S^*[2..8])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
# После получения относительного порядка суффиксов, получим тройки ('''a''', '''b''', 1) и ('''a''', '''b''', 3). Первая тройка меньше второй, поэтому добавляем суффикс, соответствующий позиции 5 в массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# В конце итерации получаем &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 3, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0.&lt;br /&gt;
К концу слияния получим &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5, 0, 3, 6, 2, 1, 4]. Так как мы добавляли один символ &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; в начале алгоритма для дополнения строки до длины, кратной трем, выбросим последний суффикс из &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;, получим в итоге, что &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [7, 5, 0, 3, 6, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; acab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; b\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; cab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
Заметим, что № 9 будет выброшен, так как в начале алгоритма был добавлен один &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt; к строке&lt;br /&gt;
 &lt;br /&gt;
{|&lt;br /&gt;
| [[Файл:Kark_sanders_stage1.png|325px|thumb| Фаза 1]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage2.png|325px|thumb| Фаза 2]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage3.png|325px|thumb| Фаза 3]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Обобщение алгоритма ==&lt;br /&gt;
Массив LCP можно получить за линейное время [[Алгоритм_Касаи_и_др. | алгоритмом Касаи]].&lt;br /&gt;
&lt;br /&gt;
На самом деле, алгоритм можно обобщить&amp;lt;ref name=&amp;quot;generalisation&amp;quot;&amp;gt; Juha Kärkkäinen, Peter Sanders and Stefan Burkhardt. Linear work suffix array construction. http://www.cs.helsinki.fi/juha.karkkainen/publications/jacm05-revised.pdf &amp;lt;/ref&amp;gt;, взяв на первом шаге, к примеру, суффиксы, позиции которых по модулю 7 дают 3, 5 и 6. Для этого потребуются некоторое усложнение алгоритма, например, сортировка оставшихся суффиксов в нескольких группах на шаге 2 и слияние нескольких групп на шаге 3, но основная идея алгоритма остается той же. Множества, которые можно выбрать, на первом шаге определяются '''разностным покрытием''' (''difference cover'').&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Разностное покрытие''' (''difference cover'') &amp;lt;tex&amp;gt; D &amp;lt;/tex&amp;gt; по модулю &amp;lt;tex&amp;gt;m &amp;lt;/tex&amp;gt; — множество чисел от &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;m - 1 &amp;lt;/tex&amp;gt; таких, что &amp;lt;tex&amp;gt; \forall i \in [0, m-1]: \exists j, k \in D: i \equiv k - j \pmod m &amp;lt;/tex&amp;gt;.&lt;br /&gt;
}} &lt;br /&gt;
Например, &amp;lt;tex&amp;gt; \{1, 2\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt; \{3, 5, 6\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt; \{1\} &amp;lt;/tex&amp;gt; — не является разностным покрытием по модулю &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;, поэтому этот алгоритм не применим к нему. Подробнее узнать, как вычислять разностное покрытие для заданного модуля можно также здесь&amp;lt;ref name=&amp;quot;generalisation&amp;quot;/&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Источники информации==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
* [http://www.cs.helsinki.fi/juha.karkkainen/publications/icalp03.pdf Juha Kärkkäinen and Peter Sanders. Simple linear work suffix array construction]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Суффиксный массив]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=48204</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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=48204"/>
				<updated>2015-06-09T06:41:01Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Алгоритм Карккайнена-Сандерса''' (Kärkkäinen, Sanders) — алгоритм построения [[суффиксный массив | суффиксного массива]] за линейное время.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Используемые обозначения ==&lt;br /&gt;
* В данном конспекте используется 0-индексация.&lt;br /&gt;
* &amp;lt;tex&amp;gt;S[i..j] &amp;lt;/tex&amp;gt; — подстрока строки &amp;lt;tex&amp;gt; S &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;-й символы включительно.&lt;br /&gt;
* Пусть длина строки &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;. Обозначим &amp;lt;tex&amp;gt; S^*[i..j] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; j \geqslant n &amp;lt;/tex&amp;gt; как строку &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt;, дополненную защитными символами &amp;lt;tex&amp;gt; \$ &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;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Четным суффиксом''' назовем суффикс, начинающийся в четной позиции. &amp;lt;br&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;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left( \dfrac{n}{2}\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для упрощения алгоритма вначале дополним нашу строку до четной длины (например, добавлением &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; в конец). На шаге слияния мы сможем избавиться от него.&lt;br /&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;
&lt;br /&gt;
# Отобразим исходную строку &amp;lt;tex&amp;gt; S^* &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; длины  &amp;lt;tex&amp;gt; \dfrac{n}{2} &amp;lt;/tex&amp;gt;  следующим образом:&lt;br /&gt;
#* Сделаем список, состоящий из пар символов вида &amp;lt;tex&amp;gt; S^*[i..i + 1] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; i \bmod 2 = 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его цифровой сортировкой за линейное время и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S^*[1..n] &amp;lt;/tex&amp;gt; в алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;, получив строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; половинной длины.&lt;br /&gt;
# Рекурсивно построим суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Построим суффиксный массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;. Очевидно, &amp;lt;tex&amp;gt; A_{S_o}[i] = 2 A_{S'}[i] + 1 &amp;lt;/tex&amp;gt;, так отношение упорядоченности любых двух строк в старом алфавите &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt; эквивалентно отношению упорядоченности в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; по его построению.&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для четных суффиксов=====&lt;br /&gt;
На этом шаге мы за линейное время получим суффиксный массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; для четных суффиксов, используя уже построенный &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что сортировка множества четных суффиксов &amp;lt;tex&amp;gt; \{ S^*[i..n] \mid i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке множества пар &amp;lt;tex&amp;gt; \{ (S^*[i], S^*[i+1..n]) \mid i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt;. Однако &amp;lt;tex&amp;gt; S^*[i+1..n] &amp;lt;/tex&amp;gt; — нечетный суффикс, и его относительную позицию мы уже узнали на шаге 1.&lt;br /&gt;
&lt;br /&gt;
Таким образом, чтобы отсортировать эти пары за линейное время, сначала сразу выпишем их в порядке возрастания второго элемента пары (то есть в порядке вхождения в массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;), а потом отсортируем устойчивой сортировкой подсчетом по первым элементам. После этого легко можно восстановить массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n / 2 - 1&lt;br /&gt;
     M.add(Pair(S[&amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 quick_stable_sort(M)&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
    &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&gt;
&lt;br /&gt;
Заметим, что массив &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; перед сортировкой подсчетом не был явно отсортирован по вторым элементам, и хранил не суффиксы, а их позиции в строке &amp;lt;tex&amp;gt; S &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;
Для суффиксного дерева третий шаг алгоритма опирается на специфические особенности суффиксных деревьев, которые не присущи суффиксным массивам&amp;lt;ref&amp;gt;M. Farach. Optimal suffix tree construction with large alphabets. http://www.cs.rutgers.edu/~farach/pubs/FarFerrMuthu00.pdf &amp;lt;/ref&amp;gt; .  &lt;br /&gt;
В случае [[Суффиксный массив|суффиксного массива]] слияние становится очень сложным, но все же оно было реализовано в алгоритме Ким-Сим-Парк-Парка&amp;lt;ref&amp;gt; D. K. Kim, J. S. Sim, H. Park, and K. Park. Linear-time construction of suffix arrays. http://www.springerlink.com/content/568156021q45r320/&amp;lt;/ref&amp;gt;. Однако простой модификацией алгоритма можно значительно упростить его.&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
Покажем первые два шага агоритма для строки '''ababbbaa'''.&lt;br /&gt;
&lt;br /&gt;
Во-первых, добавив защитный символ '''$''', получив строку '''ababbbaa$''' (для этого алгоритма он не требуется, но может понадобиться в применениях суффиксного массива). Во-вторых, дополним ее до четной длины, получив '''ababbbaa$$'''.&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для нечетных суффиксов=====&lt;br /&gt;
# В новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет четыре элемента — '''ba''', '''bb''', '''a$''', '''$$'''. После сортировки они получат номера 2, 3, 1 и 0 соответственно.&lt;br /&gt;
# Переводим строку &amp;lt;tex&amp;gt;S^*[1..n]&amp;lt;/tex&amp;gt; = '''babbbaa$$$''' в новый алфавит. Сжатой строкой &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; будет '''23210'''.&lt;br /&gt;
# После рекурсивного вызова получим, что &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [4, 3, 2, 0, 1], и &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; = [9, 7, 5, 1, 3].&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для четных суффиксов=====&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [(&amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt;, 9), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 7), (&amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, 5), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 1), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 3)].&lt;br /&gt;
# После сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt;= [(&amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt;, 9), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 7), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 1), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 3), (&amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, 5)].&lt;br /&gt;
# Восстановив массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;, получаем [8, 6, 0, 2, 4], что действительно является суффиксным массивом для четных суффиксов.&lt;br /&gt;
&lt;br /&gt;
=====Слияние суффиксных массивов=====&lt;br /&gt;
Если бы мы умели сливать &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; за линейное время, получили бы:&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 9 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; a\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; aa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ababbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; baa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; babbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Как и было сказано вначале, избавиться от лишних '''$''' легко, так как суффиксы, им соответствующие, будут первыми в суффиксном массиве (в данном случае достаточно выбросить &amp;quot;9&amp;quot; из суффиксного массива).&lt;br /&gt;
&lt;br /&gt;
== Алгоритм Карккайнена-Сандерса == &lt;br /&gt;
Изменим изначальный алгоритм следующим образом:&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих позициям, не кратным трем. Рекурсивно сведем это к построению суффиксного массива для строки длиной в две трети исходной.&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих кратным трем позициям, используя результат первого шага, за линейное время.&lt;br /&gt;
# Сольем эти суффиксные массивы в один за линейное время.&lt;br /&gt;
&lt;br /&gt;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left(\dfrac23 n\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого также является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt; (это видно из того, что сумма геометрической прогрессии с основанием &amp;lt;tex&amp;gt; \dfrac23 &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;).&lt;br /&gt;
&lt;br /&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;S^*&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* '''База рекурсии'''&lt;br /&gt;
Для этого алгоритма минимальной базой рекурсии будет строка длиной 4, так как она дополняется до длины 6, после чего вновь следует рекурсивный вызов строки длиной 4, и, если бы база была меньше 4, алгоритм вошел бы в бесконечную рекурсию. На этом этапе также можно применить обычную сортировку суффиксов, так как это потребует &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
На этом шаге строится суффиксный массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; для множества суффиксов &amp;lt;tex&amp;gt; \{ S^*[i..n-1] \mid i \bmod 3 \ne 0 \} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Получим строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; аналогично предыдущему алгоритму:&lt;br /&gt;
#* Сделаем список, состоящий из троек &amp;lt;tex&amp;gt; S^*[i..i+2]&amp;lt;/tex&amp;gt; , где &amp;lt;tex&amp;gt; i \bmod 3 \ne 0 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его за линейное время цифровой сортировкой и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S^*[1..n]S^*[2..n+1] &amp;lt;/tex&amp;gt; в строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; длиной &amp;lt;tex&amp;gt; \dfrac23 n &amp;lt;/tex&amp;gt; в алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;. Тогда суффиксу &amp;lt;tex&amp;gt; S^*[i..n-1] &amp;lt;/tex&amp;gt; в старом алфавите, где &amp;lt;tex&amp;gt; i \bmod 3 = 1 &amp;lt;/tex&amp;gt;, в новом алфавите будет соответствовать строка &amp;lt;tex&amp;gt; S'\left[\dfrac{i-1}{3}..\dfrac{n}{3} - 1\right] &amp;lt;/tex&amp;gt;, а если &amp;lt;tex&amp;gt; i\bmod 3 = 2 &amp;lt;/tex&amp;gt;, то строка &amp;lt;tex&amp;gt; S'\left[\dfrac{n}{3} + \dfrac{i-2}{3}..\dfrac{2n}{3} - 1\right] &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Вызовем алгоритм рекурсивно для строки &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt;, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Пройдем по массиву &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt; A_{S'}[i] &amp;lt; \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j = 3A_{S'}[i] + 1 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;, если же &amp;lt;tex&amp;gt; A_{S'}[i] \geqslant \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j =  3\left(A_{S'}[i] - \dfrac{n}{3}\right) + 2 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S^* &amp;lt;/tex&amp;gt;. Псевдокод получения &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;.length - 1:&lt;br /&gt;
    '''if''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] &amp;lt; n/3:&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] + 1)&lt;br /&gt;
    '''else''':&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * (&amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] - n/3) + 2)&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
Этот шаг также аналогичен первой версии алгоритма. Сортировка множества &amp;lt;tex&amp;gt; \{ S^*[i..n-1] \mid i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке пар &amp;lt;tex&amp;gt; \{ (S^*[i], S^*[i+1..n-1]) \mid i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; S^*[i+1..n-1] &amp;lt;/tex&amp;gt; — суффиксы в позициях, равных 1 по модулю 3, относительный порядок которых уже известен. Выпишем эти пары в порядке вхождения их в &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и отсортируем по первому элементу устойчивой сортировкой подсчетом, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;. Псевдокод этого шага:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' 2n/3 - 1:&lt;br /&gt;
     '''if''' &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[i] % 3 == 1:&lt;br /&gt;
         M.add(Pair(S*[&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 '''for''' i = 0 '''to''' n/3 - 1:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&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; A_{S_0} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, чтобы получить суффиксный массив &amp;lt;tex&amp;gt; A_{S} &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;
&lt;br /&gt;
Пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 1 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Это аналогично сравнению пар &amp;lt;tex&amp;gt; (S^*[i], S^*[i+1..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S^*[j], S^*[j+1..n-1]) &amp;lt;/tex&amp;gt;. Сравнить первые элементы пар мы можем за &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt;, а относительный порядок вторых элементов пар нам уже известен, так как они соответствуют позициям, равным 2 и 1 по модулю 3 соответственно.&lt;br /&gt;
&lt;br /&gt;
Аналогично, пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 2 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Тогда это аналогично сравнению троек &amp;lt;tex&amp;gt; (S^*[i], S^*[i+1], S^*[i+2..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S^*[j], S^*[j+1], S^*[j+2..n-1]) &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;A_{S}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 // Вначале предподсчитаем за O(n) обратную перестановку для суффиксного массива &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;, то есть массив rank такой, что &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[rank[i]] = i.&lt;br /&gt;
 // Тогда мы сможем за O(1) сравнивать суффиксы по их позиции.&lt;br /&gt;
 rank = inverse(&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;) &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3 '''and''' j &amp;lt; n/3:&lt;br /&gt;
     pos12 = &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i]&lt;br /&gt;
     pos0  = &amp;lt;tex&amp;gt; A_{0} &amp;lt;/tex&amp;gt;[j]&lt;br /&gt;
     '''if''' pos12 % 3 == 1:&lt;br /&gt;
        ''' if''' Pair(S*[pos12], rank[pos12 + 1]) &amp;lt; Pair(S*[pos0], rank[pos0 + 1]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++  &lt;br /&gt;
     '''else''':&lt;br /&gt;
         '''if''' Triple(S*[pos12], S*[pos12 + 1], rank[pos12 + 2]) &amp;lt; Triple(S*[pos0], S*[pos0 + 1], rank[pos0 + 2]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++ &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i])&lt;br /&gt;
     i++&lt;br /&gt;
 '''while''' j &amp;lt; n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{0}} &amp;lt;/tex&amp;gt;[j])&lt;br /&gt;
     j++&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;
Построим суффиксный массив для строки '''abbacab'''. После добавления защитного символа и дополнения до кратной трем длины, получим '''abbacab$$'''.&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
# Тройками, соответствующими равными 1 по модулю 3 позициям, будут: '''bba''', '''cab''', '''$$$''', соответствующими равным 2 по модулю 3 — '''bac''', '''cab$''', '''$$$'''. Новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет содержать элементы '''bba''', '''cab''', '''$$$''', '''bac''', '''ab$''', которые после сортировки получат номера 3, 4, 0, 2, 1 соответственно.&lt;br /&gt;
# Строкой '''bbacab$$$bacab$$$$''' в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; = 340210. &lt;br /&gt;
# После рекурсивного вызова получим &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [5, 2, 4, 3, 0, 1]. Пересчитав &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, получим [(5 - 3)*3 + 2, 2 * 3 + 1, (4 - 3) * 3 + 2, (3 - 3) * 3 + 2, 0 * 3 + 1, 1 * 3 + 1] = [8, 7, 5, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и выбрав в нем элементы, равные 1 по модулю 3, получим массив пар &amp;lt;tex&amp;gt;M&amp;lt;/tex&amp;gt; = [(&amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, 7), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 1), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 4)]&lt;br /&gt;
# После устойчивой сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''a''', 1), ('''a''', 4), ('''b''', 7)]&lt;br /&gt;
# Восстановив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;, получаем [0, 3, 6].&lt;br /&gt;
* '''Слияние суффиксных массивов'''&lt;br /&gt;
Рассмотрим, к примеру, третью итерацию слияния, к этой итерации массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 2, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0, на ней мы сливаем суффиксы, соответствующие позициям 5 и 0. &lt;br /&gt;
# Образуем тройки &amp;lt;tex&amp;gt;(S^*[5], S^*[6], S^*[7..8])&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;(S^*[0], S^*[1], S^*[2..8])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
# После получения относительного порядка суффиксов, получим тройки ('''a''', '''b''', 1) и ('''a''', '''b''', 3). Первая тройка меньше второй, поэтому добавляем суффикс, соответствующий позиции 5 в массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# В конце итерации получаем &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 3, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0.&lt;br /&gt;
К концу слияния получим &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5, 0, 3, 6, 2, 1, 4]. Так как мы добавляли один символ &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; в начале алгоритма для дополнения строки до длины, кратной трем, выбросим последний суффикс из &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;, получим в итоге, что &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [7, 5, 0, 3, 6, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; acab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; b\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; cab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
Заметим, что № 9 будет выброшен, так как в начале алгоритма был добавлен один &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt; к строке&lt;br /&gt;
 &lt;br /&gt;
{|&lt;br /&gt;
| [[Файл:Kark_sanders_stage1.png|325px|thumb| Фаза 1]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage2.png|325px|thumb| Фаза 2]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage3.png|325px|thumb| Фаза 3]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Обобщение алгоритма ==&lt;br /&gt;
Массив LCP можно получить за линейное время [[Алгоритм_Касаи_и_др. | алгоритмом Касаи]].&lt;br /&gt;
&lt;br /&gt;
На самом деле, алгоритм можно обобщить&amp;lt;ref name=&amp;quot;generalisation&amp;quot;&amp;gt; Juha Kärkkäinen, Peter Sanders and Stefan Burkhardt. Linear work suffix array construction. http://www.cs.helsinki.fi/juha.karkkainen/publications/jacm05-revised.pdf &amp;lt;/ref&amp;gt;, взяв на первом шаге, к примеру, суффиксы, позиции которых по модулю 7 дают 3, 5 и 6. Для этого потребуются некоторое усложнение алгоритма, например, сортировка оставшихся суффиксов в нескольких группах на шаге 2 и слияние нескольких групп на шаге 3, но основная идея алгоритма остается той же. Множества, которые можно выбрать, на первом шаге определяются '''разностным покрытием''' (''difference cover'').&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Разностное покрытие''' (''difference cover'') &amp;lt;tex&amp;gt; D &amp;lt;/tex&amp;gt; по модулю &amp;lt;tex&amp;gt;m &amp;lt;/tex&amp;gt; — множество чисел от &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;m - 1 &amp;lt;/tex&amp;gt; таких, что &amp;lt;tex&amp;gt; \forall i \in [0, m-1]: \exists j, k \in D: i \equiv k - j \pmod m &amp;lt;/tex&amp;gt;.&lt;br /&gt;
}} &lt;br /&gt;
Например, &amp;lt;tex&amp;gt; \{1, 2\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt; \{3, 5, 6\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt; \{1\} &amp;lt;/tex&amp;gt; — не является разностным покрытием по модулю &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;, поэтому этот алгоритм не применим к нему. Подробнее узнать, как вычислять разностное покрытие для заданного модуля можно также здесь&amp;lt;ref name=&amp;quot;generalisation&amp;quot;/&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Источники информации==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
* [http://www.cs.helsinki.fi/juha.karkkainen/publications/icalp03.pdf Juha Kärkkäinen and Peter Sanders. Simple linear work suffix array construction]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Суффиксный массив]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=48203</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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=48203"/>
				<updated>2015-06-09T06:35:45Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Суффиксный массив для четных суффиксов */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Алгоритм Карккайнена-Сандерса''' (Kärkkäinen, Sanders) — алгоритм построения [[суффиксный массив | суффиксного массива]] за линейное время.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Используемые обозначения ==&lt;br /&gt;
* В данном конспекте используется 0-индексация.&lt;br /&gt;
* &amp;lt;tex&amp;gt;S[i..j] &amp;lt;/tex&amp;gt; — подстрока строки &amp;lt;tex&amp;gt; S &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;-й символы включительно.&lt;br /&gt;
* Пусть длина строки &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;. Обозначим &amp;lt;tex&amp;gt; S^*[i..j] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; j \geqslant n &amp;lt;/tex&amp;gt; как строку &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt;, дополненную защитными символами &amp;lt;tex&amp;gt; \$ &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;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Четным суффиксом''' назовем суффикс, начинающийся в четной позиции. &amp;lt;br&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;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left( \dfrac{n}{2}\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для упрощения алгоритма вначале дополним нашу строку до четной длины (например, добавлением &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; в конец). На шаге слияния мы сможем избавиться от него.&lt;br /&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;
&lt;br /&gt;
# Отобразим исходную строку &amp;lt;tex&amp;gt; S^* &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; длины  &amp;lt;tex&amp;gt; \dfrac{n}{2} &amp;lt;/tex&amp;gt;  следующим образом:&lt;br /&gt;
#* Сделаем список, состоящий из пар символов вида &amp;lt;tex&amp;gt; S^*[i..i + 1] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; i \bmod 2 = 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его цифровой сортировкой за линейное время и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S^*[1..n] &amp;lt;/tex&amp;gt; в алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;, получив строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; половинной длины.&lt;br /&gt;
# Рекурсивно построим суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Построим суффиксный массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;. Очевидно, &amp;lt;tex&amp;gt; A_{S_o}[i] = 2 A_{S'}[i] + 1 &amp;lt;/tex&amp;gt;, так отношение упорядоченности любых двух строк в старом алфавите &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt; эквивалентно отношению упорядоченности в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; по его построению.&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для четных суффиксов=====&lt;br /&gt;
На этом шаге мы за линейное время получим суффиксный массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; для четных суффиксов, используя уже построенный &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что сортировка множества четных суффиксов &amp;lt;tex&amp;gt; \{ S^*[i..n] \mid i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке множества пар &amp;lt;tex&amp;gt; \{ (S^*[i], S^*[i+1..n]) \mid i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt;. Однако &amp;lt;tex&amp;gt; S^*[i+1..n] &amp;lt;/tex&amp;gt; — нечетный суффикс, и его относительную позицию мы уже узнали на шаге 1.&lt;br /&gt;
&lt;br /&gt;
Таким образом, чтобы отсортировать эти пары за линейное время, сначала сразу выпишем их в порядке возрастания второго элемента пары (то есть в порядке вхождения в массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;), а потом отсортируем устойчивой сортировкой подсчетом по первым элементам. После этого легко можно восстановить массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n / 2 - 1&lt;br /&gt;
     M.add(Pair(S[&amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 quick_stable_sort(M)&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
    &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&gt;
&lt;br /&gt;
Заметим, что массив &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; перед сортировкой подсчетом не был явно отсортирован по вторым элементам, и хранил не суффиксы, а их позиции в строке &amp;lt;tex&amp;gt; S &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;
Для суффиксного дерева третий шаг алгоритма опирается на специфические особенности суффиксных деревьев, которые не присущи суффиксным массивам&amp;lt;ref&amp;gt;M. Farach. Optimal suffix tree construction with large alphabets. http://www.cs.rutgers.edu/~farach/pubs/FarFerrMuthu00.pdf &amp;lt;/ref&amp;gt; .  &lt;br /&gt;
В случае [[Суффиксный массив|суффиксного массива]] слияние становится очень сложным, но все же оно было реализовано в алгоритме Ким-Сим-Парк-Парка&amp;lt;ref&amp;gt; D. K. Kim, J. S. Sim, H. Park, and K. Park. Linear-time construction of suffix arrays. http://www.springerlink.com/content/568156021q45r320/&amp;lt;/ref&amp;gt;. Однако простой модификацией алгоритма можно значительно упростить его.&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
Покажем первые два шага агоритма для строки '''ababbbaa'''.&lt;br /&gt;
&lt;br /&gt;
Во-первых, добавив защитный символ '''$''', получив строку '''ababbbaa$''' (для этого алгоритма он не требуется, но может понадобиться в применениях суффиксного массива). Во-вторых, дополним ее до четной длины, получив '''ababbbaa$$'''.&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для нечетных суффиксов=====&lt;br /&gt;
# В новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет четыре элемента — '''ba''', '''bb''', '''a$''', '''$$'''. После сортировки они получат номера 2, 3, 1 и 0 соответственно.&lt;br /&gt;
# Переводим строку &amp;lt;tex&amp;gt;S^*[1..n]&amp;lt;/tex&amp;gt; = '''babbbaa$$$''' в новый алфавит. Сжатой строкой &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; будет '''23210'''.&lt;br /&gt;
# После рекурсивного вызова получим, что &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [4, 3, 2, 0, 1], и &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; = [9, 7, 5, 1, 3].&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для четных суффиксов=====&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [(&amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt;, 9), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 7), (&amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, 5), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 1), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 3)].&lt;br /&gt;
# После сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt;= [(&amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt;, 9), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 7), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 1), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 3), (&amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, 5)].&lt;br /&gt;
# Восстановив массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;, получаем [8, 6, 0, 2, 4], что действительно является суффиксным массивом для четных суффиксов.&lt;br /&gt;
&lt;br /&gt;
=====Слияние суффиксных массивов=====&lt;br /&gt;
Если бы мы умели сливать &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; за линейное время, получили бы:&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 9 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; a\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; aa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ababbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; baa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; babbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Как и было сказано вначале, избавиться от лишних '''$''' легко, так как суффиксы, им соответствующие, будут первыми в суффиксном массиве (в данном случае достаточно выбросить &amp;quot;9&amp;quot; из суффиксного массива).&lt;br /&gt;
&lt;br /&gt;
== Алгоритм Карккайнена-Сандерса == &lt;br /&gt;
Изменим изначальный алгоритм следующим образом:&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих позициям, не кратным трем. Рекурсивно сведем это к построению суффиксного массива для строки длиной в две трети исходной.&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих кратным трем позициям, используя результат первого шага, за линейное время.&lt;br /&gt;
# Сольем эти суффиксные массивы в один за линейное время.&lt;br /&gt;
&lt;br /&gt;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left(\dfrac23 n\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого также является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt; (это видно из того, что сумма геометрической прогрессии с основанием &amp;lt;tex&amp;gt; \dfrac23 &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;).&lt;br /&gt;
&lt;br /&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;S^*&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* '''База рекурсии'''&lt;br /&gt;
Для этого алгоритма минимальной базой рекурсии будет строка длиной 4, так как она дополняется до длины 6, после чего вновь следует рекурсивный вызов строки длиной 4, и, если бы база была меньше 4, алгоритм вошел бы в бесконечную рекурсию. На этом этапе также можно применить обычную сортировку суффиксов, так как это потребует &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
На этом шаге строится суффиксный массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; для множества суффиксов &amp;lt;tex&amp;gt; \{ S^*[i..n-1] \mid i \bmod 3 \ne 0 \} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Получим строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; аналогично предыдущему алгоритму:&lt;br /&gt;
#* Сделаем список, состоящий из троек &amp;lt;tex&amp;gt; S^*[i..i+2]&amp;lt;/tex&amp;gt; , где &amp;lt;tex&amp;gt; i \bmod 3 \ne 0 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его за линейное время цифровой сортировкой и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S^*[1..n]S^*[2..n+1] &amp;lt;/tex&amp;gt; в строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; длиной &amp;lt;tex&amp;gt; \dfrac23 n &amp;lt;/tex&amp;gt; в алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;. Тогда суффиксу &amp;lt;tex&amp;gt; S^*[i..n-1] &amp;lt;/tex&amp;gt; в старом алфавите, где &amp;lt;tex&amp;gt; i \bmod 3 = 1 &amp;lt;/tex&amp;gt;, в новом алфавите будет соответствовать строка &amp;lt;tex&amp;gt; S'\left[\dfrac{i-1}{3}..\dfrac{n}{3} - 1\right] &amp;lt;/tex&amp;gt;, а если &amp;lt;tex&amp;gt; i\bmod 3 = 2 &amp;lt;/tex&amp;gt;, то строка &amp;lt;tex&amp;gt; S'\left[\dfrac{n}{3} + \dfrac{i-2}{3}..\dfrac{2n}{3} - 1\right] &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Вызовем алгоритм рекурсивно для строки &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt;, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Пройдем по массиву &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt; A_{S'}[i] &amp;lt; \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j = 3A_{S'}[i] + 1 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;, если же &amp;lt;tex&amp;gt; A_{S'}[i] \geqslant \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j =  3\left(A_{S'}[i] - \dfrac{n}{3}\right) + 2 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S^* &amp;lt;/tex&amp;gt;. Псевдокод получения &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;.length - 1:&lt;br /&gt;
    '''if''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] &amp;lt; n/3:&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] + 1)&lt;br /&gt;
    '''else''':&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * (&amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] - n/3) + 2)&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
Этот шаг также аналогичен первой версии алгоритма. Сортировка множества &amp;lt;tex&amp;gt; \{ S^*[i..n-1] \mid i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке пар &amp;lt;tex&amp;gt; \{ (S^*[i], S^*[i+1..n-1]) \mid i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; S^*[i+1..n-1] &amp;lt;/tex&amp;gt; — суффиксы в позициях, равных 1 по модулю 3, относительный порядок которых уже известен. Выпишем эти пары в порядке вхождения их в &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и отсортируем по первому элементу устойчивой сортировкой подсчетом, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;. Псевдокод этого шага:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' 2n/3 - 1:&lt;br /&gt;
     '''if''' &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[i] % 3 == 1:&lt;br /&gt;
         M.add(Pair(S*[&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 '''for''' i = 0 '''to''' n/3 - 1:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&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; A_{S_0} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, чтобы получить суффиксный массив &amp;lt;tex&amp;gt; A_{S} &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;
&lt;br /&gt;
Пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 1 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Это аналогично сравнению пар &amp;lt;tex&amp;gt; (S^*[i], S^*[i+1..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S^*[j], S^*[j+1..n-1]) &amp;lt;/tex&amp;gt;. Сравнить первые элементы пар мы можем за &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt;, а относительный порядок вторых элементов пар нам уже известен, так как они соответствуют позициям, равным 2 и 1 по модулю 3 соответственно.&lt;br /&gt;
&lt;br /&gt;
Аналогично, пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 2 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Тогда это аналогично сравнению троек &amp;lt;tex&amp;gt; (S^*[i], S^*[i+1], S^*[i+2..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S^*[j], S^*[j+1], S^*[j+2..n-1]) &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;A_{S}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 // Вначале предподсчитаем за O(n) обратную перестановку для суффиксного массива &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;, то есть массив rank такой, что &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[rank[i]] = i.&lt;br /&gt;
 // Тогда мы сможем за O(1) сравнивать суффиксы по их позиции.&lt;br /&gt;
 rank = inverse(&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;) &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3 '''and''' j &amp;lt; n/3:&lt;br /&gt;
     pos12 = &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i]&lt;br /&gt;
     pos0  = &amp;lt;tex&amp;gt; A_{0} &amp;lt;/tex&amp;gt;[j]&lt;br /&gt;
     '''if''' pos12 % 3 == 1:&lt;br /&gt;
        ''' if''' Pair(S*[pos12], rank[pos12 + 1]) &amp;lt; Pair(S*[pos0], rank[pos0 + 1]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++  &lt;br /&gt;
     '''else''':&lt;br /&gt;
         '''if''' Triple(S*[pos12], S*[pos12 + 1], rank[pos12 + 2]) &amp;lt; Triple(S*[pos0], S*[pos0 + 1], rank[pos0 + 2]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++ &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i])&lt;br /&gt;
     i++&lt;br /&gt;
 '''while''' j &amp;lt; n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{0}} &amp;lt;/tex&amp;gt;[j])&lt;br /&gt;
     j++&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;
Построим суффиксный массив для строки '''abbacab'''. После добавления защитного символа и дополнения до кратной трем длины, получим '''abbacab$$'''.&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
# Тройками, соответствующими равными 1 по модулю 3 позициям, будут: '''bba''', '''cab''', '''$$$''', соответствующими равным 2 по модулю 3 — '''bac''', '''cab$''', '''$$$'''. Новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет содержать элементы '''bba''', '''cab''', '''$$$''', '''bac''', '''ab$''', которые после сортировки получат номера 3, 4, 0, 2, 1 соответственно.&lt;br /&gt;
# Строкой '''bbacab$$$bacab$$$$''' в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; = 340210. &lt;br /&gt;
# После рекурсивного вызова получим &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [5, 2, 4, 3, 0, 1]. Пересчитав &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, получим [(5 - 3)*3 + 2, 2 * 3 + 1, (4 - 3) * 3 + 2, (3 - 3) * 3 + 2, 0 * 3 + 1, 1 * 3 + 1] = [8, 7, 5, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и выбрав в нем элементы, равные 1 по модулю 3, получим массив пар &amp;lt;tex&amp;gt;M&amp;lt;/tex&amp;gt; = [(&amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, 7), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 1), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 4)]&lt;br /&gt;
# После устойчивой сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''a''', 1), ('''a''', 4), ('''b''', 7)]&lt;br /&gt;
# Восстановив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;, получаем [0, 3, 6].&lt;br /&gt;
* '''Слияние суффиксных массивов'''&lt;br /&gt;
Рассмотрим, к примеру, третью итерацию слияния, к этой итерации массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 2, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0, на ней мы сливаем суффиксы, соответствующие позициям 5 и 0. &lt;br /&gt;
# Образуем тройки &amp;lt;tex&amp;gt;(S^*[5], S^*[6], S^*[7..8])&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;(S^*[0], S^*[1], S^*[2..8])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
# После получения относительного порядка суффиксов, получим тройки ('''a''', '''b''', 1) и ('''a''', '''b''', 3). Первая тройка меньше второй, поэтому добавляем суффикс, соответствующий позиции 5 в массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# В конце итерации получаем &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 3, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0.&lt;br /&gt;
К концу слияния получим &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5, 0, 3, 6, 2, 1, 4]. Так как мы добавляли один символ &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; в начале алгоритма для дополнения строки до длины, кратной трем, выбросим последний суффикс из &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;, получим в итоге, что &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [7, 5, 0, 3, 6, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; acab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; b\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; cab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
Заметим, что № 9 будет выброшен, так как в начале алгоритма был добавлен один &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt; к строке&lt;br /&gt;
 &lt;br /&gt;
{|&lt;br /&gt;
| [[Файл:Kark_sanders_stage1.png|325px|thumb| Фаза 1]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage2.png|325px|thumb| Фаза 2]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage3.png|325px|thumb| Фаза 3]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Получение массива LCP ==&lt;br /&gt;
Массив LCP можно получить за линейное время [[Алгоритм_Касаи_и_др. | алгоритмом Касаи]].&lt;br /&gt;
&lt;br /&gt;
== Обобщение алгоритма ==&lt;br /&gt;
На самом деле, алгоритм можно обобщить&amp;lt;ref name=&amp;quot;generalisation&amp;quot;&amp;gt; Juha Kärkkäinen, Peter Sanders and Stefan Burkhardt. Linear work suffix array construction. http://www.cs.helsinki.fi/juha.karkkainen/publications/jacm05-revised.pdf &amp;lt;/ref&amp;gt;, взяв на первом шаге, к примеру, суффиксы, позиции которых по модулю 7 дают 3, 5 и 6. Для этого потребуются некоторое усложнение алгоритма, например, сортировка оставшихся суффиксов в нескольких группах на шаге 2 и слияние нескольких групп на шаге 3, но основная идея алгоритма остается той же. Множества, которые можно выбрать, на первом шаге определяются '''разностным покрытием''' (''difference cover'').&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Разностное покрытие''' (''difference cover'') &amp;lt;tex&amp;gt; D &amp;lt;/tex&amp;gt; по модулю &amp;lt;tex&amp;gt;m &amp;lt;/tex&amp;gt; — множество чисел от &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;m - 1 &amp;lt;/tex&amp;gt; таких, что &amp;lt;tex&amp;gt; \forall i \in [0, m-1]: \exists j, k \in D: i \equiv k - j \pmod m &amp;lt;/tex&amp;gt;.&lt;br /&gt;
}} &lt;br /&gt;
Например, &amp;lt;tex&amp;gt; \{1, 2\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt; \{3, 5, 6\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt; \{1\} &amp;lt;/tex&amp;gt; — не является разностным покрытием по модулю &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;, поэтому этот алгоритм не применим к нему. Подробнее узнать, как вычислять разностное покрытие для заданного модуля можно также здесь&amp;lt;ref name=&amp;quot;generalisation&amp;quot;/&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Источники информации==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
* [http://www.cs.helsinki.fi/juha.karkkainen/publications/icalp03.pdf Juha Kärkkäinen and Peter Sanders. Simple linear work suffix array construction]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Суффиксный массив]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=48202</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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=48202"/>
				<updated>2015-06-09T06:33:34Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Слияние суффиксных массивов */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Алгоритм Карккайнена-Сандерса''' (Kärkkäinen, Sanders) — алгоритм построения [[суффиксный массив | суффиксного массива]] за линейное время.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Используемые обозначения ==&lt;br /&gt;
* В данном конспекте используется 0-индексация.&lt;br /&gt;
* &amp;lt;tex&amp;gt;S[i..j] &amp;lt;/tex&amp;gt; — подстрока строки &amp;lt;tex&amp;gt; S &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;-й символы включительно.&lt;br /&gt;
* Пусть длина строки &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;. Обозначим &amp;lt;tex&amp;gt; S^*[i..j] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; j \geqslant n &amp;lt;/tex&amp;gt; как строку &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt;, дополненную защитными символами &amp;lt;tex&amp;gt; \$ &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;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Четным суффиксом''' назовем суффикс, начинающийся в четной позиции. &amp;lt;br&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;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left( \dfrac{n}{2}\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для упрощения алгоритма вначале дополним нашу строку до четной длины (например, добавлением &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; в конец). На шаге слияния мы сможем избавиться от него.&lt;br /&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;
&lt;br /&gt;
# Отобразим исходную строку &amp;lt;tex&amp;gt; S^* &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; длины  &amp;lt;tex&amp;gt; \dfrac{n}{2} &amp;lt;/tex&amp;gt;  следующим образом:&lt;br /&gt;
#* Сделаем список, состоящий из пар символов вида &amp;lt;tex&amp;gt; S^*[i..i + 1] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; i \bmod 2 = 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его цифровой сортировкой за линейное время и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S^*[1..n] &amp;lt;/tex&amp;gt; в алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;, получив строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; половинной длины.&lt;br /&gt;
# Рекурсивно построим суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Построим суффиксный массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;. Очевидно, &amp;lt;tex&amp;gt; A_{S_o}[i] = 2 A_{S'}[i] + 1 &amp;lt;/tex&amp;gt;, так отношение упорядоченности любых двух строк в старом алфавите &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt; эквивалентно отношению упорядоченности в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; по его построению.&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для четных суффиксов=====&lt;br /&gt;
На этом шаге мы за линейное время получим суффиксный массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; для четных суффиксов, используя уже построенный &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что сортировка множества четных суффиксов &amp;lt;tex&amp;gt; \{ S^*[i..n] \mid i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке множества пар &amp;lt;tex&amp;gt; \{ (S^*[i], S^*[i+1..n]) \mid i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt;. Однако &amp;lt;tex&amp;gt; S^*[i+1..n] &amp;lt;/tex&amp;gt; — нечетный суффикс, и его относительную позицию мы уже узнали на шаге 1.&lt;br /&gt;
&lt;br /&gt;
Таким образом, чтобы отсортировать эти пары за линейное время, сначала сразу выпишем их в порядке возрастания второго элемента пары (то есть в порядке вхождения в массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;), а потом отсортируем устойчивой сортировкой подсчетом по первым элементам. После этого легко можно восстановить массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n / 2 - 1&lt;br /&gt;
     M.add(Pair(S[&amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 quick_stable_sort(M)&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
    &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&gt;
&lt;br /&gt;
Заметим, что массив &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; перед сортировкой подсчетом не был явно отсортирован по вторым элементам, и хранил не суффиксы, а их позиции в строке &amp;lt;tex&amp;gt; S &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;
Для суффиксного дерева третий шаг алгоритма опирается на специфические особенности суффиксных деревьев, которые не присущи суффиксным массивам&amp;lt;ref&amp;gt;M. Farach. Optimal suffix tree construction with large alphabets. http://www.cs.rutgers.edu/~farach/pubs/FarFerrMuthu00.pdf &amp;lt;/ref&amp;gt; .  &lt;br /&gt;
В случае [[Суффиксный массив|суффиксного массива]] слияние становится очень сложным, но все же оно было реализовано в алгоритме Ким-Сим-Парк-Парка&amp;lt;ref&amp;gt; D. K. Kim, J. S. Sim, H. Park, and K. Park. Linear-time construction of suffix arrays. http://www.springerlink.com/content/568156021q45r320/&amp;lt;/ref&amp;gt;. Однако простой модификацией алгоритма можно значительно упростить его.&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
Покажем первые два шага агоритма для строки '''ababbbaa'''.&lt;br /&gt;
&lt;br /&gt;
Во-первых, добавив защитный символ '''$''', получив строку '''ababbbaa$''' (для этого алгоритма он не требуется, но может понадобиться в применениях суффиксного массива). Во-вторых, дополним ее до четной длины, получив '''ababbbaa$$'''.&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для нечетных суффиксов=====&lt;br /&gt;
# В новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет четыре элемента — '''ba''', '''bb''', '''a$''', '''$$'''. После сортировки они получат номера 2, 3, 1 и 0 соответственно.&lt;br /&gt;
# Переводим строку &amp;lt;tex&amp;gt;S^*[1..n]&amp;lt;/tex&amp;gt; = '''babbbaa$$$''' в новый алфавит. Сжатой строкой &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; будет '''23210'''.&lt;br /&gt;
# После рекурсивного вызова получим, что &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [4, 3, 2, 0, 1], и &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; = [9, 7, 5, 1, 3].&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для четных суффиксов=====&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''$''', 9), ('''a''', 7), ('''b''', 5), ('''a''', 1), ('''a''', 3)].&lt;br /&gt;
# После сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt;= [('''$''', 9), ('''a''', 7), ('''a''', 1), ('''a''', 3), ('''b''', 5)].&lt;br /&gt;
# Восстановив массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;, получаем [8, 6, 0, 2, 4], что действительно является суффиксным массивом для четных суффиксов.&lt;br /&gt;
&lt;br /&gt;
=====Слияние суффиксных массивов=====&lt;br /&gt;
Если бы мы умели сливать &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; за линейное время, получили бы:&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 9 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; a\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; aa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ababbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; baa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; babbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Как и было сказано вначале, избавиться от лишних '''$''' легко, так как суффиксы, им соответствующие, будут первыми в суффиксном массиве (в данном случае достаточно выбросить &amp;quot;9&amp;quot; из суффиксного массива).&lt;br /&gt;
&lt;br /&gt;
== Алгоритм Карккайнена-Сандерса == &lt;br /&gt;
Изменим изначальный алгоритм следующим образом:&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих позициям, не кратным трем. Рекурсивно сведем это к построению суффиксного массива для строки длиной в две трети исходной.&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих кратным трем позициям, используя результат первого шага, за линейное время.&lt;br /&gt;
# Сольем эти суффиксные массивы в один за линейное время.&lt;br /&gt;
&lt;br /&gt;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left(\dfrac23 n\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого также является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt; (это видно из того, что сумма геометрической прогрессии с основанием &amp;lt;tex&amp;gt; \dfrac23 &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;).&lt;br /&gt;
&lt;br /&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;S^*&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* '''База рекурсии'''&lt;br /&gt;
Для этого алгоритма минимальной базой рекурсии будет строка длиной 4, так как она дополняется до длины 6, после чего вновь следует рекурсивный вызов строки длиной 4, и, если бы база была меньше 4, алгоритм вошел бы в бесконечную рекурсию. На этом этапе также можно применить обычную сортировку суффиксов, так как это потребует &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
На этом шаге строится суффиксный массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; для множества суффиксов &amp;lt;tex&amp;gt; \{ S^*[i..n-1] \mid i \bmod 3 \ne 0 \} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Получим строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; аналогично предыдущему алгоритму:&lt;br /&gt;
#* Сделаем список, состоящий из троек &amp;lt;tex&amp;gt; S^*[i..i+2]&amp;lt;/tex&amp;gt; , где &amp;lt;tex&amp;gt; i \bmod 3 \ne 0 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его за линейное время цифровой сортировкой и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S^*[1..n]S^*[2..n+1] &amp;lt;/tex&amp;gt; в строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; длиной &amp;lt;tex&amp;gt; \dfrac23 n &amp;lt;/tex&amp;gt; в алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;. Тогда суффиксу &amp;lt;tex&amp;gt; S^*[i..n-1] &amp;lt;/tex&amp;gt; в старом алфавите, где &amp;lt;tex&amp;gt; i \bmod 3 = 1 &amp;lt;/tex&amp;gt;, в новом алфавите будет соответствовать строка &amp;lt;tex&amp;gt; S'\left[\dfrac{i-1}{3}..\dfrac{n}{3} - 1\right] &amp;lt;/tex&amp;gt;, а если &amp;lt;tex&amp;gt; i\bmod 3 = 2 &amp;lt;/tex&amp;gt;, то строка &amp;lt;tex&amp;gt; S'\left[\dfrac{n}{3} + \dfrac{i-2}{3}..\dfrac{2n}{3} - 1\right] &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Вызовем алгоритм рекурсивно для строки &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt;, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Пройдем по массиву &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt; A_{S'}[i] &amp;lt; \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j = 3A_{S'}[i] + 1 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;, если же &amp;lt;tex&amp;gt; A_{S'}[i] \geqslant \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j =  3\left(A_{S'}[i] - \dfrac{n}{3}\right) + 2 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S^* &amp;lt;/tex&amp;gt;. Псевдокод получения &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;.length - 1:&lt;br /&gt;
    '''if''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] &amp;lt; n/3:&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] + 1)&lt;br /&gt;
    '''else''':&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * (&amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] - n/3) + 2)&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
Этот шаг также аналогичен первой версии алгоритма. Сортировка множества &amp;lt;tex&amp;gt; \{ S^*[i..n-1] \mid i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке пар &amp;lt;tex&amp;gt; \{ (S^*[i], S^*[i+1..n-1]) \mid i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; S^*[i+1..n-1] &amp;lt;/tex&amp;gt; — суффиксы в позициях, равных 1 по модулю 3, относительный порядок которых уже известен. Выпишем эти пары в порядке вхождения их в &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и отсортируем по первому элементу устойчивой сортировкой подсчетом, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;. Псевдокод этого шага:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' 2n/3 - 1:&lt;br /&gt;
     '''if''' &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[i] % 3 == 1:&lt;br /&gt;
         M.add(Pair(S*[&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 '''for''' i = 0 '''to''' n/3 - 1:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&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; A_{S_0} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, чтобы получить суффиксный массив &amp;lt;tex&amp;gt; A_{S} &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;
&lt;br /&gt;
Пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 1 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Это аналогично сравнению пар &amp;lt;tex&amp;gt; (S^*[i], S^*[i+1..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S^*[j], S^*[j+1..n-1]) &amp;lt;/tex&amp;gt;. Сравнить первые элементы пар мы можем за &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt;, а относительный порядок вторых элементов пар нам уже известен, так как они соответствуют позициям, равным 2 и 1 по модулю 3 соответственно.&lt;br /&gt;
&lt;br /&gt;
Аналогично, пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 2 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Тогда это аналогично сравнению троек &amp;lt;tex&amp;gt; (S^*[i], S^*[i+1], S^*[i+2..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S^*[j], S^*[j+1], S^*[j+2..n-1]) &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;A_{S}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 // Вначале предподсчитаем за O(n) обратную перестановку для суффиксного массива &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;, то есть массив rank такой, что &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[rank[i]] = i.&lt;br /&gt;
 // Тогда мы сможем за O(1) сравнивать суффиксы по их позиции.&lt;br /&gt;
 rank = inverse(&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;) &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3 '''and''' j &amp;lt; n/3:&lt;br /&gt;
     pos12 = &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i]&lt;br /&gt;
     pos0  = &amp;lt;tex&amp;gt; A_{0} &amp;lt;/tex&amp;gt;[j]&lt;br /&gt;
     '''if''' pos12 % 3 == 1:&lt;br /&gt;
        ''' if''' Pair(S*[pos12], rank[pos12 + 1]) &amp;lt; Pair(S*[pos0], rank[pos0 + 1]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++  &lt;br /&gt;
     '''else''':&lt;br /&gt;
         '''if''' Triple(S*[pos12], S*[pos12 + 1], rank[pos12 + 2]) &amp;lt; Triple(S*[pos0], S*[pos0 + 1], rank[pos0 + 2]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++ &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i])&lt;br /&gt;
     i++&lt;br /&gt;
 '''while''' j &amp;lt; n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{0}} &amp;lt;/tex&amp;gt;[j])&lt;br /&gt;
     j++&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;
Построим суффиксный массив для строки '''abbacab'''. После добавления защитного символа и дополнения до кратной трем длины, получим '''abbacab$$'''.&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
# Тройками, соответствующими равными 1 по модулю 3 позициям, будут: '''bba''', '''cab''', '''$$$''', соответствующими равным 2 по модулю 3 — '''bac''', '''cab$''', '''$$$'''. Новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет содержать элементы '''bba''', '''cab''', '''$$$''', '''bac''', '''ab$''', которые после сортировки получат номера 3, 4, 0, 2, 1 соответственно.&lt;br /&gt;
# Строкой '''bbacab$$$bacab$$$$''' в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; = 340210. &lt;br /&gt;
# После рекурсивного вызова получим &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [5, 2, 4, 3, 0, 1]. Пересчитав &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, получим [(5 - 3)*3 + 2, 2 * 3 + 1, (4 - 3) * 3 + 2, (3 - 3) * 3 + 2, 0 * 3 + 1, 1 * 3 + 1] = [8, 7, 5, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и выбрав в нем элементы, равные 1 по модулю 3, получим массив пар &amp;lt;tex&amp;gt;M&amp;lt;/tex&amp;gt; = [(&amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, 7), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 1), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 4)]&lt;br /&gt;
# После устойчивой сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''a''', 1), ('''a''', 4), ('''b''', 7)]&lt;br /&gt;
# Восстановив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;, получаем [0, 3, 6].&lt;br /&gt;
* '''Слияние суффиксных массивов'''&lt;br /&gt;
Рассмотрим, к примеру, третью итерацию слияния, к этой итерации массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 2, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0, на ней мы сливаем суффиксы, соответствующие позициям 5 и 0. &lt;br /&gt;
# Образуем тройки &amp;lt;tex&amp;gt;(S^*[5], S^*[6], S^*[7..8])&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;(S^*[0], S^*[1], S^*[2..8])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
# После получения относительного порядка суффиксов, получим тройки ('''a''', '''b''', 1) и ('''a''', '''b''', 3). Первая тройка меньше второй, поэтому добавляем суффикс, соответствующий позиции 5 в массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# В конце итерации получаем &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 3, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0.&lt;br /&gt;
К концу слияния получим &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5, 0, 3, 6, 2, 1, 4]. Так как мы добавляли один символ &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; в начале алгоритма для дополнения строки до длины, кратной трем, выбросим последний суффикс из &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;, получим в итоге, что &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [7, 5, 0, 3, 6, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; acab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; b\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; cab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
Заметим, что № 9 будет выброшен, так как в начале алгоритма был добавлен один &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt; к строке&lt;br /&gt;
 &lt;br /&gt;
{|&lt;br /&gt;
| [[Файл:Kark_sanders_stage1.png|325px|thumb| Фаза 1]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage2.png|325px|thumb| Фаза 2]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage3.png|325px|thumb| Фаза 3]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Получение массива LCP ==&lt;br /&gt;
Массив LCP можно получить за линейное время [[Алгоритм_Касаи_и_др. | алгоритмом Касаи]].&lt;br /&gt;
&lt;br /&gt;
== Обобщение алгоритма ==&lt;br /&gt;
На самом деле, алгоритм можно обобщить&amp;lt;ref name=&amp;quot;generalisation&amp;quot;&amp;gt; Juha Kärkkäinen, Peter Sanders and Stefan Burkhardt. Linear work suffix array construction. http://www.cs.helsinki.fi/juha.karkkainen/publications/jacm05-revised.pdf &amp;lt;/ref&amp;gt;, взяв на первом шаге, к примеру, суффиксы, позиции которых по модулю 7 дают 3, 5 и 6. Для этого потребуются некоторое усложнение алгоритма, например, сортировка оставшихся суффиксов в нескольких группах на шаге 2 и слияние нескольких групп на шаге 3, но основная идея алгоритма остается той же. Множества, которые можно выбрать, на первом шаге определяются '''разностным покрытием''' (''difference cover'').&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Разностное покрытие''' (''difference cover'') &amp;lt;tex&amp;gt; D &amp;lt;/tex&amp;gt; по модулю &amp;lt;tex&amp;gt;m &amp;lt;/tex&amp;gt; — множество чисел от &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;m - 1 &amp;lt;/tex&amp;gt; таких, что &amp;lt;tex&amp;gt; \forall i \in [0, m-1]: \exists j, k \in D: i \equiv k - j \pmod m &amp;lt;/tex&amp;gt;.&lt;br /&gt;
}} &lt;br /&gt;
Например, &amp;lt;tex&amp;gt; \{1, 2\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt; \{3, 5, 6\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt; \{1\} &amp;lt;/tex&amp;gt; — не является разностным покрытием по модулю &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;, поэтому этот алгоритм не применим к нему. Подробнее узнать, как вычислять разностное покрытие для заданного модуля можно также здесь&amp;lt;ref name=&amp;quot;generalisation&amp;quot;/&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Источники информации==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
* [http://www.cs.helsinki.fi/juha.karkkainen/publications/icalp03.pdf Juha Kärkkäinen and Peter Sanders. Simple linear work suffix array construction]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Суффиксный массив]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=48201</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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=48201"/>
				<updated>2015-06-09T06:29:51Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Суффиксный массив для четных суффиксов */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Алгоритм Карккайнена-Сандерса''' (Kärkkäinen, Sanders) — алгоритм построения [[суффиксный массив | суффиксного массива]] за линейное время.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Используемые обозначения ==&lt;br /&gt;
* В данном конспекте используется 0-индексация.&lt;br /&gt;
* &amp;lt;tex&amp;gt;S[i..j] &amp;lt;/tex&amp;gt; — подстрока строки &amp;lt;tex&amp;gt; S &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;-й символы включительно.&lt;br /&gt;
* Пусть длина строки &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;. Обозначим &amp;lt;tex&amp;gt; S^*[i..j] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; j \geqslant n &amp;lt;/tex&amp;gt; как строку &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt;, дополненную защитными символами &amp;lt;tex&amp;gt; \$ &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;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Четным суффиксом''' назовем суффикс, начинающийся в четной позиции. &amp;lt;br&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;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left( \dfrac{n}{2}\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для упрощения алгоритма вначале дополним нашу строку до четной длины (например, добавлением &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; в конец). На шаге слияния мы сможем избавиться от него.&lt;br /&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;
&lt;br /&gt;
# Отобразим исходную строку &amp;lt;tex&amp;gt; S^* &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; длины  &amp;lt;tex&amp;gt; \dfrac{n}{2} &amp;lt;/tex&amp;gt;  следующим образом:&lt;br /&gt;
#* Сделаем список, состоящий из пар символов вида &amp;lt;tex&amp;gt; S^*[i..i + 1] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; i \bmod 2 = 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его цифровой сортировкой за линейное время и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S^*[1..n] &amp;lt;/tex&amp;gt; в алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;, получив строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; половинной длины.&lt;br /&gt;
# Рекурсивно построим суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Построим суффиксный массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;. Очевидно, &amp;lt;tex&amp;gt; A_{S_o}[i] = 2 A_{S'}[i] + 1 &amp;lt;/tex&amp;gt;, так отношение упорядоченности любых двух строк в старом алфавите &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt; эквивалентно отношению упорядоченности в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; по его построению.&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для четных суффиксов=====&lt;br /&gt;
На этом шаге мы за линейное время получим суффиксный массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; для четных суффиксов, используя уже построенный &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что сортировка множества четных суффиксов &amp;lt;tex&amp;gt; \{ S^*[i..n] \mid i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке множества пар &amp;lt;tex&amp;gt; \{ (S^*[i], S^*[i+1..n]) \mid i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt;. Однако &amp;lt;tex&amp;gt; S^*[i+1..n] &amp;lt;/tex&amp;gt; — нечетный суффикс, и его относительную позицию мы уже узнали на шаге 1.&lt;br /&gt;
&lt;br /&gt;
Таким образом, чтобы отсортировать эти пары за линейное время, сначала сразу выпишем их в порядке возрастания второго элемента пары (то есть в порядке вхождения в массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;), а потом отсортируем устойчивой сортировкой подсчетом по первым элементам. После этого легко можно восстановить массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n / 2 - 1&lt;br /&gt;
     M.add(Pair(S[&amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 quick_stable_sort(M)&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
    &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&gt;
&lt;br /&gt;
Заметим, что массив &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; перед сортировкой подсчетом не был явно отсортирован по вторым элементам, и хранил не суффиксы, а их позиции в строке &amp;lt;tex&amp;gt; S &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;
Для суффиксного дерева третий шаг алгоритма опирается на специфические особенности суффиксных деревьев, которые не присущи суффиксным массивам&amp;lt;ref&amp;gt;M. Farach. Optimal suffix tree construction with large alphabets. http://www.cs.rutgers.edu/~farach/pubs/FarFerrMuthu00.pdf &amp;lt;/ref&amp;gt; .  &lt;br /&gt;
В случае суффиксного массива слияние становится очень сложным, но все же оно было реализовано в алгоритме Ким-Сим-Парк-Парка&amp;lt;ref&amp;gt; D. K. Kim, J. S. Sim, H. Park, and K. Park. Linear-time construction of suffix arrays. http://www.springerlink.com/content/568156021q45r320/&amp;lt;/ref&amp;gt;. Однако простой модификацией алгоритма можно значительно упростить его.&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
Покажем первые два шага агоритма для строки '''ababbbaa'''.&lt;br /&gt;
&lt;br /&gt;
Во-первых, добавив защитный символ '''$''', получив строку '''ababbbaa$''' (для этого алгоритма он не требуется, но может понадобиться в применениях суффиксного массива). Во-вторых, дополним ее до четной длины, получив '''ababbbaa$$'''.&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для нечетных суффиксов=====&lt;br /&gt;
# В новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет четыре элемента — '''ba''', '''bb''', '''a$''', '''$$'''. После сортировки они получат номера 2, 3, 1 и 0 соответственно.&lt;br /&gt;
# Переводим строку &amp;lt;tex&amp;gt;S^*[1..n]&amp;lt;/tex&amp;gt; = '''babbbaa$$$''' в новый алфавит. Сжатой строкой &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; будет '''23210'''.&lt;br /&gt;
# После рекурсивного вызова получим, что &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [4, 3, 2, 0, 1], и &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; = [9, 7, 5, 1, 3].&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для четных суффиксов=====&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''$''', 9), ('''a''', 7), ('''b''', 5), ('''a''', 1), ('''a''', 3)].&lt;br /&gt;
# После сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt;= [('''$''', 9), ('''a''', 7), ('''a''', 1), ('''a''', 3), ('''b''', 5)].&lt;br /&gt;
# Восстановив массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;, получаем [8, 6, 0, 2, 4], что действительно является суффиксным массивом для четных суффиксов.&lt;br /&gt;
&lt;br /&gt;
=====Слияние суффиксных массивов=====&lt;br /&gt;
Если бы мы умели сливать &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; за линейное время, получили бы:&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 9 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; a\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; aa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ababbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; baa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; babbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Как и было сказано вначале, избавиться от лишних '''$''' легко, так как суффиксы, им соответствующие, будут первыми в суффиксном массиве (в данном случае достаточно выбросить &amp;quot;9&amp;quot; из суффиксного массива).&lt;br /&gt;
&lt;br /&gt;
== Алгоритм Карккайнена-Сандерса == &lt;br /&gt;
Изменим изначальный алгоритм следующим образом:&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих позициям, не кратным трем. Рекурсивно сведем это к построению суффиксного массива для строки длиной в две трети исходной.&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих кратным трем позициям, используя результат первого шага, за линейное время.&lt;br /&gt;
# Сольем эти суффиксные массивы в один за линейное время.&lt;br /&gt;
&lt;br /&gt;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left(\dfrac23 n\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого также является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt; (это видно из того, что сумма геометрической прогрессии с основанием &amp;lt;tex&amp;gt; \dfrac23 &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;).&lt;br /&gt;
&lt;br /&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;S^*&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* '''База рекурсии'''&lt;br /&gt;
Для этого алгоритма минимальной базой рекурсии будет строка длиной 4, так как она дополняется до длины 6, после чего вновь следует рекурсивный вызов строки длиной 4, и, если бы база была меньше 4, алгоритм вошел бы в бесконечную рекурсию. На этом этапе также можно применить обычную сортировку суффиксов, так как это потребует &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
На этом шаге строится суффиксный массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; для множества суффиксов &amp;lt;tex&amp;gt; \{ S^*[i..n-1] \mid i \bmod 3 \ne 0 \} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Получим строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; аналогично предыдущему алгоритму:&lt;br /&gt;
#* Сделаем список, состоящий из троек &amp;lt;tex&amp;gt; S^*[i..i+2]&amp;lt;/tex&amp;gt; , где &amp;lt;tex&amp;gt; i \bmod 3 \ne 0 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его за линейное время цифровой сортировкой и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S^*[1..n]S^*[2..n+1] &amp;lt;/tex&amp;gt; в строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; длиной &amp;lt;tex&amp;gt; \dfrac23 n &amp;lt;/tex&amp;gt; в алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;. Тогда суффиксу &amp;lt;tex&amp;gt; S^*[i..n-1] &amp;lt;/tex&amp;gt; в старом алфавите, где &amp;lt;tex&amp;gt; i \bmod 3 = 1 &amp;lt;/tex&amp;gt;, в новом алфавите будет соответствовать строка &amp;lt;tex&amp;gt; S'\left[\dfrac{i-1}{3}..\dfrac{n}{3} - 1\right] &amp;lt;/tex&amp;gt;, а если &amp;lt;tex&amp;gt; i\bmod 3 = 2 &amp;lt;/tex&amp;gt;, то строка &amp;lt;tex&amp;gt; S'\left[\dfrac{n}{3} + \dfrac{i-2}{3}..\dfrac{2n}{3} - 1\right] &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Вызовем алгоритм рекурсивно для строки &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt;, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Пройдем по массиву &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt; A_{S'}[i] &amp;lt; \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j = 3A_{S'}[i] + 1 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;, если же &amp;lt;tex&amp;gt; A_{S'}[i] \geqslant \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j =  3\left(A_{S'}[i] - \dfrac{n}{3}\right) + 2 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S^* &amp;lt;/tex&amp;gt;. Псевдокод получения &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;.length - 1:&lt;br /&gt;
    '''if''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] &amp;lt; n/3:&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] + 1)&lt;br /&gt;
    '''else''':&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * (&amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] - n/3) + 2)&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
Этот шаг также аналогичен первой версии алгоритма. Сортировка множества &amp;lt;tex&amp;gt; \{ S^*[i..n-1] \mid i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке пар &amp;lt;tex&amp;gt; \{ (S^*[i], S^*[i+1..n-1]) \mid i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; S^*[i+1..n-1] &amp;lt;/tex&amp;gt; — суффиксы в позициях, равных 1 по модулю 3, относительный порядок которых уже известен. Выпишем эти пары в порядке вхождения их в &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и отсортируем по первому элементу устойчивой сортировкой подсчетом, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;. Псевдокод этого шага:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' 2n/3 - 1:&lt;br /&gt;
     '''if''' &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[i] % 3 == 1:&lt;br /&gt;
         M.add(Pair(S*[&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 '''for''' i = 0 '''to''' n/3 - 1:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&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; A_{S_0} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, чтобы получить суффиксный массив &amp;lt;tex&amp;gt; A_{S} &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;
&lt;br /&gt;
Пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 1 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Это аналогично сравнению пар &amp;lt;tex&amp;gt; (S^*[i], S^*[i+1..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S^*[j], S^*[j+1..n-1]) &amp;lt;/tex&amp;gt;. Сравнить первые элементы пар мы можем за &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt;, а относительный порядок вторых элементов пар нам уже известен, так как они соответствуют позициям, равным 2 и 1 по модулю 3 соответственно.&lt;br /&gt;
&lt;br /&gt;
Аналогично, пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 2 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Тогда это аналогично сравнению троек &amp;lt;tex&amp;gt; (S^*[i], S^*[i+1], S^*[i+2..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S^*[j], S^*[j+1], S^*[j+2..n-1]) &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;A_{S}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 // Вначале предподсчитаем за O(n) обратную перестановку для суффиксного массива &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;, то есть массив rank такой, что &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[rank[i]] = i.&lt;br /&gt;
 // Тогда мы сможем за O(1) сравнивать суффиксы по их позиции.&lt;br /&gt;
 rank = inverse(&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;) &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3 '''and''' j &amp;lt; n/3:&lt;br /&gt;
     pos12 = &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i]&lt;br /&gt;
     pos0  = &amp;lt;tex&amp;gt; A_{0} &amp;lt;/tex&amp;gt;[j]&lt;br /&gt;
     '''if''' pos12 % 3 == 1:&lt;br /&gt;
        ''' if''' Pair(S*[pos12], rank[pos12 + 1]) &amp;lt; Pair(S*[pos0], rank[pos0 + 1]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++  &lt;br /&gt;
     '''else''':&lt;br /&gt;
         '''if''' Triple(S*[pos12], S*[pos12 + 1], rank[pos12 + 2]) &amp;lt; Triple(S*[pos0], S*[pos0 + 1], rank[pos0 + 2]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++ &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i])&lt;br /&gt;
     i++&lt;br /&gt;
 '''while''' j &amp;lt; n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{0}} &amp;lt;/tex&amp;gt;[j])&lt;br /&gt;
     j++&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;
Построим суффиксный массив для строки '''abbacab'''. После добавления защитного символа и дополнения до кратной трем длины, получим '''abbacab$$'''.&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
# Тройками, соответствующими равными 1 по модулю 3 позициям, будут: '''bba''', '''cab''', '''$$$''', соответствующими равным 2 по модулю 3 — '''bac''', '''cab$''', '''$$$'''. Новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет содержать элементы '''bba''', '''cab''', '''$$$''', '''bac''', '''ab$''', которые после сортировки получат номера 3, 4, 0, 2, 1 соответственно.&lt;br /&gt;
# Строкой '''bbacab$$$bacab$$$$''' в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; = 340210. &lt;br /&gt;
# После рекурсивного вызова получим &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [5, 2, 4, 3, 0, 1]. Пересчитав &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, получим [(5 - 3)*3 + 2, 2 * 3 + 1, (4 - 3) * 3 + 2, (3 - 3) * 3 + 2, 0 * 3 + 1, 1 * 3 + 1] = [8, 7, 5, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и выбрав в нем элементы, равные 1 по модулю 3, получим массив пар &amp;lt;tex&amp;gt;M&amp;lt;/tex&amp;gt; = [(&amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, 7), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 1), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 4)]&lt;br /&gt;
# После устойчивой сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''a''', 1), ('''a''', 4), ('''b''', 7)]&lt;br /&gt;
# Восстановив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;, получаем [0, 3, 6].&lt;br /&gt;
* '''Слияние суффиксных массивов'''&lt;br /&gt;
Рассмотрим, к примеру, третью итерацию слияния, к этой итерации массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 2, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0, на ней мы сливаем суффиксы, соответствующие позициям 5 и 0. &lt;br /&gt;
# Образуем тройки &amp;lt;tex&amp;gt;(S^*[5], S^*[6], S^*[7..8])&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;(S^*[0], S^*[1], S^*[2..8])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
# После получения относительного порядка суффиксов, получим тройки ('''a''', '''b''', 1) и ('''a''', '''b''', 3). Первая тройка меньше второй, поэтому добавляем суффикс, соответствующий позиции 5 в массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# В конце итерации получаем &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 3, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0.&lt;br /&gt;
К концу слияния получим &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5, 0, 3, 6, 2, 1, 4]. Так как мы добавляли один символ &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; в начале алгоритма для дополнения строки до длины, кратной трем, выбросим последний суффикс из &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;, получим в итоге, что &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [7, 5, 0, 3, 6, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; acab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; b\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; cab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
Заметим, что № 9 будет выброшен, так как в начале алгоритма был добавлен один &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt; к строке&lt;br /&gt;
 &lt;br /&gt;
{|&lt;br /&gt;
| [[Файл:Kark_sanders_stage1.png|325px|thumb| Фаза 1]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage2.png|325px|thumb| Фаза 2]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage3.png|325px|thumb| Фаза 3]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Получение массива LCP ==&lt;br /&gt;
Массив LCP можно получить за линейное время [[Алгоритм_Касаи_и_др. | алгоритмом Касаи]].&lt;br /&gt;
&lt;br /&gt;
== Обобщение алгоритма ==&lt;br /&gt;
На самом деле, алгоритм можно обобщить&amp;lt;ref name=&amp;quot;generalisation&amp;quot;&amp;gt; Juha Kärkkäinen, Peter Sanders and Stefan Burkhardt. Linear work suffix array construction. http://www.cs.helsinki.fi/juha.karkkainen/publications/jacm05-revised.pdf &amp;lt;/ref&amp;gt;, взяв на первом шаге, к примеру, суффиксы, позиции которых по модулю 7 дают 3, 5 и 6. Для этого потребуются некоторое усложнение алгоритма, например, сортировка оставшихся суффиксов в нескольких группах на шаге 2 и слияние нескольких групп на шаге 3, но основная идея алгоритма остается той же. Множества, которые можно выбрать, на первом шаге определяются '''разностным покрытием''' (''difference cover'').&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Разностное покрытие''' (''difference cover'') &amp;lt;tex&amp;gt; D &amp;lt;/tex&amp;gt; по модулю &amp;lt;tex&amp;gt;m &amp;lt;/tex&amp;gt; — множество чисел от &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;m - 1 &amp;lt;/tex&amp;gt; таких, что &amp;lt;tex&amp;gt; \forall i \in [0, m-1]: \exists j, k \in D: i \equiv k - j \pmod m &amp;lt;/tex&amp;gt;.&lt;br /&gt;
}} &lt;br /&gt;
Например, &amp;lt;tex&amp;gt; \{1, 2\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt; \{3, 5, 6\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt; \{1\} &amp;lt;/tex&amp;gt; — не является разностным покрытием по модулю &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;, поэтому этот алгоритм не применим к нему. Подробнее узнать, как вычислять разностное покрытие для заданного модуля можно также здесь&amp;lt;ref name=&amp;quot;generalisation&amp;quot;/&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Источники информации==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
* [http://www.cs.helsinki.fi/juha.karkkainen/publications/icalp03.pdf Juha Kärkkäinen and Peter Sanders. Simple linear work suffix array construction]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Суффиксный массив]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=48200</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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=48200"/>
				<updated>2015-06-09T06:28:29Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Суффиксный массив для четных суффиксов */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Алгоритм Карккайнена-Сандерса''' (Kärkkäinen, Sanders) — алгоритм построения [[суффиксный массив | суффиксного массива]] за линейное время.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Используемые обозначения ==&lt;br /&gt;
* В данном конспекте используется 0-индексация.&lt;br /&gt;
* &amp;lt;tex&amp;gt;S[i..j] &amp;lt;/tex&amp;gt; — подстрока строки &amp;lt;tex&amp;gt; S &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;-й символы включительно.&lt;br /&gt;
* Пусть длина строки &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;. Обозначим &amp;lt;tex&amp;gt; S^*[i..j] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; j \geqslant n &amp;lt;/tex&amp;gt; как строку &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt;, дополненную защитными символами &amp;lt;tex&amp;gt; \$ &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;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Четным суффиксом''' назовем суффикс, начинающийся в четной позиции. &amp;lt;br&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;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left( \dfrac{n}{2}\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для упрощения алгоритма вначале дополним нашу строку до четной длины (например, добавлением &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; в конец). На шаге слияния мы сможем избавиться от него.&lt;br /&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;
&lt;br /&gt;
# Отобразим исходную строку &amp;lt;tex&amp;gt; S^* &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; длины  &amp;lt;tex&amp;gt; \dfrac{n}{2} &amp;lt;/tex&amp;gt;  следующим образом:&lt;br /&gt;
#* Сделаем список, состоящий из пар символов вида &amp;lt;tex&amp;gt; S^*[i..i + 1] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; i \bmod 2 = 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его цифровой сортировкой за линейное время и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S^*[1..n] &amp;lt;/tex&amp;gt; в алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;, получив строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; половинной длины.&lt;br /&gt;
# Рекурсивно построим суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Построим суффиксный массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;. Очевидно, &amp;lt;tex&amp;gt; A_{S_o}[i] = 2 A_{S'}[i] + 1 &amp;lt;/tex&amp;gt;, так отношение упорядоченности любых двух строк в старом алфавите &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt; эквивалентно отношению упорядоченности в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; по его построению.&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для четных суффиксов=====&lt;br /&gt;
На этом шаге мы за линейное время получим суффиксный массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; для четных суффиксов, используя уже построенный &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что сортировка множества четных суффиксов &amp;lt;tex&amp;gt; \{ S^*[i..n] \mid i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке множества пар &amp;lt;tex&amp;gt; \{ (S^*[i], S^*[i+1..n]) \mid i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt;. Однако &amp;lt;tex&amp;gt; S^*[i+1..n] &amp;lt;/tex&amp;gt; — нечетный суффикс, и его относительную позицию мы уже узнали на шаге 1.&lt;br /&gt;
&lt;br /&gt;
Таким образом, чтобы отсортировать эти пары за линейное время, сначала сразу выпишем их в порядке возрастания второго элемента пары (то есть в порядке вхождения в массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;), а потом отсортируем устойчивой сортировкой подсчетом по первым элементам. После этого легко можно восстановить массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
     M.add(Pair(S[&amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 quick_stable_sort(M)&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
    &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&gt;
&lt;br /&gt;
Заметим, что массив &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; перед сортировкой подсчетом не был явно отсортирован по вторым элементам, и хранил не суффиксы, а их позиции в строке &amp;lt;tex&amp;gt; S &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;
Для суффиксного дерева третий шаг алгоритма опирается на специфические особенности суффиксных деревьев, которые не присущи суффиксным массивам&amp;lt;ref&amp;gt;M. Farach. Optimal suffix tree construction with large alphabets. http://www.cs.rutgers.edu/~farach/pubs/FarFerrMuthu00.pdf &amp;lt;/ref&amp;gt; .  &lt;br /&gt;
В случае суффиксного массива слияние становится очень сложным, но все же оно было реализовано в алгоритме Ким-Сим-Парк-Парка&amp;lt;ref&amp;gt; D. K. Kim, J. S. Sim, H. Park, and K. Park. Linear-time construction of suffix arrays. http://www.springerlink.com/content/568156021q45r320/&amp;lt;/ref&amp;gt;. Однако простой модификацией алгоритма можно значительно упростить его.&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
Покажем первые два шага агоритма для строки '''ababbbaa'''.&lt;br /&gt;
&lt;br /&gt;
Во-первых, добавив защитный символ '''$''', получив строку '''ababbbaa$''' (для этого алгоритма он не требуется, но может понадобиться в применениях суффиксного массива). Во-вторых, дополним ее до четной длины, получив '''ababbbaa$$'''.&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для нечетных суффиксов=====&lt;br /&gt;
# В новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет четыре элемента — '''ba''', '''bb''', '''a$''', '''$$'''. После сортировки они получат номера 2, 3, 1 и 0 соответственно.&lt;br /&gt;
# Переводим строку &amp;lt;tex&amp;gt;S^*[1..n]&amp;lt;/tex&amp;gt; = '''babbbaa$$$''' в новый алфавит. Сжатой строкой &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; будет '''23210'''.&lt;br /&gt;
# После рекурсивного вызова получим, что &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [4, 3, 2, 0, 1], и &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; = [9, 7, 5, 1, 3].&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для четных суффиксов=====&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''$''', 9), ('''a''', 7), ('''b''', 5), ('''a''', 1), ('''a''', 3)].&lt;br /&gt;
# После сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt;= [('''$''', 9), ('''a''', 7), ('''a''', 1), ('''a''', 3), ('''b''', 5)].&lt;br /&gt;
# Восстановив массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;, получаем [8, 6, 0, 2, 4], что действительно является суффиксным массивом для четных суффиксов.&lt;br /&gt;
&lt;br /&gt;
=====Слияние суффиксных массивов=====&lt;br /&gt;
Если бы мы умели сливать &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; за линейное время, получили бы:&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 9 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; a\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; aa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ababbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; baa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; babbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Как и было сказано вначале, избавиться от лишних '''$''' легко, так как суффиксы, им соответствующие, будут первыми в суффиксном массиве (в данном случае достаточно выбросить &amp;quot;9&amp;quot; из суффиксного массива).&lt;br /&gt;
&lt;br /&gt;
== Алгоритм Карккайнена-Сандерса == &lt;br /&gt;
Изменим изначальный алгоритм следующим образом:&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих позициям, не кратным трем. Рекурсивно сведем это к построению суффиксного массива для строки длиной в две трети исходной.&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих кратным трем позициям, используя результат первого шага, за линейное время.&lt;br /&gt;
# Сольем эти суффиксные массивы в один за линейное время.&lt;br /&gt;
&lt;br /&gt;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left(\dfrac23 n\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого также является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt; (это видно из того, что сумма геометрической прогрессии с основанием &amp;lt;tex&amp;gt; \dfrac23 &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;).&lt;br /&gt;
&lt;br /&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;S^*&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* '''База рекурсии'''&lt;br /&gt;
Для этого алгоритма минимальной базой рекурсии будет строка длиной 4, так как она дополняется до длины 6, после чего вновь следует рекурсивный вызов строки длиной 4, и, если бы база была меньше 4, алгоритм вошел бы в бесконечную рекурсию. На этом этапе также можно применить обычную сортировку суффиксов, так как это потребует &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
На этом шаге строится суффиксный массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; для множества суффиксов &amp;lt;tex&amp;gt; \{ S^*[i..n-1] \mid i \bmod 3 \ne 0 \} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Получим строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; аналогично предыдущему алгоритму:&lt;br /&gt;
#* Сделаем список, состоящий из троек &amp;lt;tex&amp;gt; S^*[i..i+2]&amp;lt;/tex&amp;gt; , где &amp;lt;tex&amp;gt; i \bmod 3 \ne 0 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его за линейное время цифровой сортировкой и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S^*[1..n]S^*[2..n+1] &amp;lt;/tex&amp;gt; в строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; длиной &amp;lt;tex&amp;gt; \dfrac23 n &amp;lt;/tex&amp;gt; в алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;. Тогда суффиксу &amp;lt;tex&amp;gt; S^*[i..n-1] &amp;lt;/tex&amp;gt; в старом алфавите, где &amp;lt;tex&amp;gt; i \bmod 3 = 1 &amp;lt;/tex&amp;gt;, в новом алфавите будет соответствовать строка &amp;lt;tex&amp;gt; S'\left[\dfrac{i-1}{3}..\dfrac{n}{3} - 1\right] &amp;lt;/tex&amp;gt;, а если &amp;lt;tex&amp;gt; i\bmod 3 = 2 &amp;lt;/tex&amp;gt;, то строка &amp;lt;tex&amp;gt; S'\left[\dfrac{n}{3} + \dfrac{i-2}{3}..\dfrac{2n}{3} - 1\right] &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Вызовем алгоритм рекурсивно для строки &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt;, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Пройдем по массиву &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt; A_{S'}[i] &amp;lt; \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j = 3A_{S'}[i] + 1 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;, если же &amp;lt;tex&amp;gt; A_{S'}[i] \geqslant \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j =  3\left(A_{S'}[i] - \dfrac{n}{3}\right) + 2 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S^* &amp;lt;/tex&amp;gt;. Псевдокод получения &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;.length - 1:&lt;br /&gt;
    '''if''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] &amp;lt; n/3:&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] + 1)&lt;br /&gt;
    '''else''':&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * (&amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] - n/3) + 2)&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
Этот шаг также аналогичен первой версии алгоритма. Сортировка множества &amp;lt;tex&amp;gt; \{ S^*[i..n-1] \mid i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке пар &amp;lt;tex&amp;gt; \{ (S^*[i], S^*[i+1..n-1]) \mid i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; S^*[i+1..n-1] &amp;lt;/tex&amp;gt; — суффиксы в позициях, равных 1 по модулю 3, относительный порядок которых уже известен. Выпишем эти пары в порядке вхождения их в &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и отсортируем по первому элементу устойчивой сортировкой подсчетом, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;. Псевдокод этого шага:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' 2n/3 - 1:&lt;br /&gt;
     '''if''' &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[i] % 3 == 1:&lt;br /&gt;
         M.add(Pair(S*[&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 '''for''' i = 0 '''to''' n/3 - 1:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&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; A_{S_0} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, чтобы получить суффиксный массив &amp;lt;tex&amp;gt; A_{S} &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;
&lt;br /&gt;
Пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 1 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Это аналогично сравнению пар &amp;lt;tex&amp;gt; (S^*[i], S^*[i+1..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S^*[j], S^*[j+1..n-1]) &amp;lt;/tex&amp;gt;. Сравнить первые элементы пар мы можем за &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt;, а относительный порядок вторых элементов пар нам уже известен, так как они соответствуют позициям, равным 2 и 1 по модулю 3 соответственно.&lt;br /&gt;
&lt;br /&gt;
Аналогично, пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 2 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Тогда это аналогично сравнению троек &amp;lt;tex&amp;gt; (S^*[i], S^*[i+1], S^*[i+2..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S^*[j], S^*[j+1], S^*[j+2..n-1]) &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;A_{S}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 // Вначале предподсчитаем за O(n) обратную перестановку для суффиксного массива &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;, то есть массив rank такой, что &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[rank[i]] = i.&lt;br /&gt;
 // Тогда мы сможем за O(1) сравнивать суффиксы по их позиции.&lt;br /&gt;
 rank = inverse(&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;) &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3 '''and''' j &amp;lt; n/3:&lt;br /&gt;
     pos12 = &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i]&lt;br /&gt;
     pos0  = &amp;lt;tex&amp;gt; A_{0} &amp;lt;/tex&amp;gt;[j]&lt;br /&gt;
     '''if''' pos12 % 3 == 1:&lt;br /&gt;
        ''' if''' Pair(S*[pos12], rank[pos12 + 1]) &amp;lt; Pair(S*[pos0], rank[pos0 + 1]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++  &lt;br /&gt;
     '''else''':&lt;br /&gt;
         '''if''' Triple(S*[pos12], S*[pos12 + 1], rank[pos12 + 2]) &amp;lt; Triple(S*[pos0], S*[pos0 + 1], rank[pos0 + 2]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++ &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i])&lt;br /&gt;
     i++&lt;br /&gt;
 '''while''' j &amp;lt; n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{0}} &amp;lt;/tex&amp;gt;[j])&lt;br /&gt;
     j++&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;
Построим суффиксный массив для строки '''abbacab'''. После добавления защитного символа и дополнения до кратной трем длины, получим '''abbacab$$'''.&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
# Тройками, соответствующими равными 1 по модулю 3 позициям, будут: '''bba''', '''cab''', '''$$$''', соответствующими равным 2 по модулю 3 — '''bac''', '''cab$''', '''$$$'''. Новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет содержать элементы '''bba''', '''cab''', '''$$$''', '''bac''', '''ab$''', которые после сортировки получат номера 3, 4, 0, 2, 1 соответственно.&lt;br /&gt;
# Строкой '''bbacab$$$bacab$$$$''' в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; = 340210. &lt;br /&gt;
# После рекурсивного вызова получим &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [5, 2, 4, 3, 0, 1]. Пересчитав &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, получим [(5 - 3)*3 + 2, 2 * 3 + 1, (4 - 3) * 3 + 2, (3 - 3) * 3 + 2, 0 * 3 + 1, 1 * 3 + 1] = [8, 7, 5, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и выбрав в нем элементы, равные 1 по модулю 3, получим массив пар &amp;lt;tex&amp;gt;M&amp;lt;/tex&amp;gt; = [(&amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, 7), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 1), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 4)]&lt;br /&gt;
# После устойчивой сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''a''', 1), ('''a''', 4), ('''b''', 7)]&lt;br /&gt;
# Восстановив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;, получаем [0, 3, 6].&lt;br /&gt;
* '''Слияние суффиксных массивов'''&lt;br /&gt;
Рассмотрим, к примеру, третью итерацию слияния, к этой итерации массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 2, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0, на ней мы сливаем суффиксы, соответствующие позициям 5 и 0. &lt;br /&gt;
# Образуем тройки &amp;lt;tex&amp;gt;(S^*[5], S^*[6], S^*[7..8])&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;(S^*[0], S^*[1], S^*[2..8])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
# После получения относительного порядка суффиксов, получим тройки ('''a''', '''b''', 1) и ('''a''', '''b''', 3). Первая тройка меньше второй, поэтому добавляем суффикс, соответствующий позиции 5 в массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# В конце итерации получаем &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 3, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0.&lt;br /&gt;
К концу слияния получим &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5, 0, 3, 6, 2, 1, 4]. Так как мы добавляли один символ &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; в начале алгоритма для дополнения строки до длины, кратной трем, выбросим последний суффикс из &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;, получим в итоге, что &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [7, 5, 0, 3, 6, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; acab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; b\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; cab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
Заметим, что № 9 будет выброшен, так как в начале алгоритма был добавлен один &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt; к строке&lt;br /&gt;
 &lt;br /&gt;
{|&lt;br /&gt;
| [[Файл:Kark_sanders_stage1.png|325px|thumb| Фаза 1]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage2.png|325px|thumb| Фаза 2]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage3.png|325px|thumb| Фаза 3]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Получение массива LCP ==&lt;br /&gt;
Массив LCP можно получить за линейное время [[Алгоритм_Касаи_и_др. | алгоритмом Касаи]].&lt;br /&gt;
&lt;br /&gt;
== Обобщение алгоритма ==&lt;br /&gt;
На самом деле, алгоритм можно обобщить&amp;lt;ref name=&amp;quot;generalisation&amp;quot;&amp;gt; Juha Kärkkäinen, Peter Sanders and Stefan Burkhardt. Linear work suffix array construction. http://www.cs.helsinki.fi/juha.karkkainen/publications/jacm05-revised.pdf &amp;lt;/ref&amp;gt;, взяв на первом шаге, к примеру, суффиксы, позиции которых по модулю 7 дают 3, 5 и 6. Для этого потребуются некоторое усложнение алгоритма, например, сортировка оставшихся суффиксов в нескольких группах на шаге 2 и слияние нескольких групп на шаге 3, но основная идея алгоритма остается той же. Множества, которые можно выбрать, на первом шаге определяются '''разностным покрытием''' (''difference cover'').&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Разностное покрытие''' (''difference cover'') &amp;lt;tex&amp;gt; D &amp;lt;/tex&amp;gt; по модулю &amp;lt;tex&amp;gt;m &amp;lt;/tex&amp;gt; — множество чисел от &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;m - 1 &amp;lt;/tex&amp;gt; таких, что &amp;lt;tex&amp;gt; \forall i \in [0, m-1]: \exists j, k \in D: i \equiv k - j \pmod m &amp;lt;/tex&amp;gt;.&lt;br /&gt;
}} &lt;br /&gt;
Например, &amp;lt;tex&amp;gt; \{1, 2\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt; \{3, 5, 6\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt; \{1\} &amp;lt;/tex&amp;gt; — не является разностным покрытием по модулю &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;, поэтому этот алгоритм не применим к нему. Подробнее узнать, как вычислять разностное покрытие для заданного модуля можно также здесь&amp;lt;ref name=&amp;quot;generalisation&amp;quot;/&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Источники информации==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
* [http://www.cs.helsinki.fi/juha.karkkainen/publications/icalp03.pdf Juha Kärkkäinen and Peter Sanders. Simple linear work suffix array construction]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Суффиксный массив]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=48199</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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=48199"/>
				<updated>2015-06-09T06:26:36Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Алгоритм Карккайнена-Сандерса */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Алгоритм Карккайнена-Сандерса''' (Kärkkäinen, Sanders) — алгоритм построения [[суффиксный массив | суффиксного массива]] за линейное время.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Используемые обозначения ==&lt;br /&gt;
* В данном конспекте используется 0-индексация.&lt;br /&gt;
* &amp;lt;tex&amp;gt;S[i..j] &amp;lt;/tex&amp;gt; — подстрока строки &amp;lt;tex&amp;gt; S &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;-й символы включительно.&lt;br /&gt;
* Пусть длина строки &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;. Обозначим &amp;lt;tex&amp;gt; S^*[i..j] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; j \geqslant n &amp;lt;/tex&amp;gt; как строку &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt;, дополненную защитными символами &amp;lt;tex&amp;gt; \$ &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;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Четным суффиксом''' назовем суффикс, начинающийся в четной позиции. &amp;lt;br&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;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left( \dfrac{n}{2}\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для упрощения алгоритма вначале дополним нашу строку до четной длины (например, добавлением &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; в конец). На шаге слияния мы сможем избавиться от него.&lt;br /&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;
&lt;br /&gt;
# Отобразим исходную строку &amp;lt;tex&amp;gt; S^* &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; длины  &amp;lt;tex&amp;gt; \dfrac{n}{2} &amp;lt;/tex&amp;gt;  следующим образом:&lt;br /&gt;
#* Сделаем список, состоящий из пар символов вида &amp;lt;tex&amp;gt; S^*[i..i + 1] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; i \bmod 2 = 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его цифровой сортировкой за линейное время и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S^*[1..n] &amp;lt;/tex&amp;gt; в алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;, получив строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; половинной длины.&lt;br /&gt;
# Рекурсивно построим суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Построим суффиксный массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;. Очевидно, &amp;lt;tex&amp;gt; A_{S_o}[i] = 2 A_{S'}[i] + 1 &amp;lt;/tex&amp;gt;, так отношение упорядоченности любых двух строк в старом алфавите &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt; эквивалентно отношению упорядоченности в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; по его построению.&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для четных суффиксов=====&lt;br /&gt;
На этом шаге мы за линейное время получим суффиксный массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; для четных суффиксов, используя уже построенный &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что сортировка множества четных суффиксов &amp;lt;tex&amp;gt; \{ S^*[i..n] \mid i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке множества пар &amp;lt;tex&amp;gt; \{ (S^*[i], S^*[i+1..n]) \mid i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt;. Однако &amp;lt;tex&amp;gt; S^*[i+1..n] &amp;lt;/tex&amp;gt; — нечетный суффикс, и его относительную позицию мы уже узнали на шаге 1.&lt;br /&gt;
&lt;br /&gt;
Таким образом, чтобы отсортировать эти пары за линейное время, сначала сразу выпишем их в порядке возрастания второго элемента пары (то есть в порядке вхождения в массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;), а потом отсортируем устойчивой сортировкой подсчетом по первым элементам. После этого легко можно восстановить массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
     M.add(Pair(S[&amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
    &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&gt;
&lt;br /&gt;
Заметим, что массив &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; перед сортировкой подсчетом не был явно отсортирован по вторым элементам, и хранил не суффиксы, а их позиции в строке &amp;lt;tex&amp;gt; S &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;
Для суффиксного дерева третий шаг алгоритма опирается на специфические особенности суффиксных деревьев, которые не присущи суффиксным массивам&amp;lt;ref&amp;gt;M. Farach. Optimal suffix tree construction with large alphabets. http://www.cs.rutgers.edu/~farach/pubs/FarFerrMuthu00.pdf &amp;lt;/ref&amp;gt; .  &lt;br /&gt;
В случае суффиксного массива слияние становится очень сложным, но все же оно было реализовано в алгоритме Ким-Сим-Парк-Парка&amp;lt;ref&amp;gt; D. K. Kim, J. S. Sim, H. Park, and K. Park. Linear-time construction of suffix arrays. http://www.springerlink.com/content/568156021q45r320/&amp;lt;/ref&amp;gt;. Однако простой модификацией алгоритма можно значительно упростить его.&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
Покажем первые два шага агоритма для строки '''ababbbaa'''.&lt;br /&gt;
&lt;br /&gt;
Во-первых, добавив защитный символ '''$''', получив строку '''ababbbaa$''' (для этого алгоритма он не требуется, но может понадобиться в применениях суффиксного массива). Во-вторых, дополним ее до четной длины, получив '''ababbbaa$$'''.&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для нечетных суффиксов=====&lt;br /&gt;
# В новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет четыре элемента — '''ba''', '''bb''', '''a$''', '''$$'''. После сортировки они получат номера 2, 3, 1 и 0 соответственно.&lt;br /&gt;
# Переводим строку &amp;lt;tex&amp;gt;S^*[1..n]&amp;lt;/tex&amp;gt; = '''babbbaa$$$''' в новый алфавит. Сжатой строкой &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; будет '''23210'''.&lt;br /&gt;
# После рекурсивного вызова получим, что &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [4, 3, 2, 0, 1], и &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; = [9, 7, 5, 1, 3].&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для четных суффиксов=====&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''$''', 9), ('''a''', 7), ('''b''', 5), ('''a''', 1), ('''a''', 3)].&lt;br /&gt;
# После сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt;= [('''$''', 9), ('''a''', 7), ('''a''', 1), ('''a''', 3), ('''b''', 5)].&lt;br /&gt;
# Восстановив массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;, получаем [8, 6, 0, 2, 4], что действительно является суффиксным массивом для четных суффиксов.&lt;br /&gt;
&lt;br /&gt;
=====Слияние суффиксных массивов=====&lt;br /&gt;
Если бы мы умели сливать &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; за линейное время, получили бы:&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 9 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; a\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; aa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ababbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; baa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; babbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Как и было сказано вначале, избавиться от лишних '''$''' легко, так как суффиксы, им соответствующие, будут первыми в суффиксном массиве (в данном случае достаточно выбросить &amp;quot;9&amp;quot; из суффиксного массива).&lt;br /&gt;
&lt;br /&gt;
== Алгоритм Карккайнена-Сандерса == &lt;br /&gt;
Изменим изначальный алгоритм следующим образом:&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих позициям, не кратным трем. Рекурсивно сведем это к построению суффиксного массива для строки длиной в две трети исходной.&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих кратным трем позициям, используя результат первого шага, за линейное время.&lt;br /&gt;
# Сольем эти суффиксные массивы в один за линейное время.&lt;br /&gt;
&lt;br /&gt;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left(\dfrac23 n\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого также является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt; (это видно из того, что сумма геометрической прогрессии с основанием &amp;lt;tex&amp;gt; \dfrac23 &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;).&lt;br /&gt;
&lt;br /&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;S^*&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* '''База рекурсии'''&lt;br /&gt;
Для этого алгоритма минимальной базой рекурсии будет строка длиной 4, так как она дополняется до длины 6, после чего вновь следует рекурсивный вызов строки длиной 4, и, если бы база была меньше 4, алгоритм вошел бы в бесконечную рекурсию. На этом этапе также можно применить обычную сортировку суффиксов, так как это потребует &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
На этом шаге строится суффиксный массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; для множества суффиксов &amp;lt;tex&amp;gt; \{ S^*[i..n-1] \mid i \bmod 3 \ne 0 \} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Получим строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; аналогично предыдущему алгоритму:&lt;br /&gt;
#* Сделаем список, состоящий из троек &amp;lt;tex&amp;gt; S^*[i..i+2]&amp;lt;/tex&amp;gt; , где &amp;lt;tex&amp;gt; i \bmod 3 \ne 0 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его за линейное время цифровой сортировкой и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S^*[1..n]S^*[2..n+1] &amp;lt;/tex&amp;gt; в строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; длиной &amp;lt;tex&amp;gt; \dfrac23 n &amp;lt;/tex&amp;gt; в алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;. Тогда суффиксу &amp;lt;tex&amp;gt; S^*[i..n-1] &amp;lt;/tex&amp;gt; в старом алфавите, где &amp;lt;tex&amp;gt; i \bmod 3 = 1 &amp;lt;/tex&amp;gt;, в новом алфавите будет соответствовать строка &amp;lt;tex&amp;gt; S'\left[\dfrac{i-1}{3}..\dfrac{n}{3} - 1\right] &amp;lt;/tex&amp;gt;, а если &amp;lt;tex&amp;gt; i\bmod 3 = 2 &amp;lt;/tex&amp;gt;, то строка &amp;lt;tex&amp;gt; S'\left[\dfrac{n}{3} + \dfrac{i-2}{3}..\dfrac{2n}{3} - 1\right] &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Вызовем алгоритм рекурсивно для строки &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt;, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Пройдем по массиву &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt; A_{S'}[i] &amp;lt; \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j = 3A_{S'}[i] + 1 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;, если же &amp;lt;tex&amp;gt; A_{S'}[i] \geqslant \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j =  3\left(A_{S'}[i] - \dfrac{n}{3}\right) + 2 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S^* &amp;lt;/tex&amp;gt;. Псевдокод получения &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;.length - 1:&lt;br /&gt;
    '''if''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] &amp;lt; n/3:&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] + 1)&lt;br /&gt;
    '''else''':&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * (&amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] - n/3) + 2)&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
Этот шаг также аналогичен первой версии алгоритма. Сортировка множества &amp;lt;tex&amp;gt; \{ S^*[i..n-1] \mid i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке пар &amp;lt;tex&amp;gt; \{ (S^*[i], S^*[i+1..n-1]) \mid i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; S^*[i+1..n-1] &amp;lt;/tex&amp;gt; — суффиксы в позициях, равных 1 по модулю 3, относительный порядок которых уже известен. Выпишем эти пары в порядке вхождения их в &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и отсортируем по первому элементу устойчивой сортировкой подсчетом, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;. Псевдокод этого шага:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' 2n/3 - 1:&lt;br /&gt;
     '''if''' &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[i] % 3 == 1:&lt;br /&gt;
         M.add(Pair(S*[&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 '''for''' i = 0 '''to''' n/3 - 1:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&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; A_{S_0} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, чтобы получить суффиксный массив &amp;lt;tex&amp;gt; A_{S} &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;
&lt;br /&gt;
Пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 1 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Это аналогично сравнению пар &amp;lt;tex&amp;gt; (S^*[i], S^*[i+1..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S^*[j], S^*[j+1..n-1]) &amp;lt;/tex&amp;gt;. Сравнить первые элементы пар мы можем за &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt;, а относительный порядок вторых элементов пар нам уже известен, так как они соответствуют позициям, равным 2 и 1 по модулю 3 соответственно.&lt;br /&gt;
&lt;br /&gt;
Аналогично, пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 2 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Тогда это аналогично сравнению троек &amp;lt;tex&amp;gt; (S^*[i], S^*[i+1], S^*[i+2..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S^*[j], S^*[j+1], S^*[j+2..n-1]) &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;A_{S}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 // Вначале предподсчитаем за O(n) обратную перестановку для суффиксного массива &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;, то есть массив rank такой, что &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[rank[i]] = i.&lt;br /&gt;
 // Тогда мы сможем за O(1) сравнивать суффиксы по их позиции.&lt;br /&gt;
 rank = inverse(&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;) &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3 '''and''' j &amp;lt; n/3:&lt;br /&gt;
     pos12 = &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i]&lt;br /&gt;
     pos0  = &amp;lt;tex&amp;gt; A_{0} &amp;lt;/tex&amp;gt;[j]&lt;br /&gt;
     '''if''' pos12 % 3 == 1:&lt;br /&gt;
        ''' if''' Pair(S*[pos12], rank[pos12 + 1]) &amp;lt; Pair(S*[pos0], rank[pos0 + 1]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++  &lt;br /&gt;
     '''else''':&lt;br /&gt;
         '''if''' Triple(S*[pos12], S*[pos12 + 1], rank[pos12 + 2]) &amp;lt; Triple(S*[pos0], S*[pos0 + 1], rank[pos0 + 2]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++ &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i])&lt;br /&gt;
     i++&lt;br /&gt;
 '''while''' j &amp;lt; n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{0}} &amp;lt;/tex&amp;gt;[j])&lt;br /&gt;
     j++&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;
Построим суффиксный массив для строки '''abbacab'''. После добавления защитного символа и дополнения до кратной трем длины, получим '''abbacab$$'''.&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
# Тройками, соответствующими равными 1 по модулю 3 позициям, будут: '''bba''', '''cab''', '''$$$''', соответствующими равным 2 по модулю 3 — '''bac''', '''cab$''', '''$$$'''. Новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет содержать элементы '''bba''', '''cab''', '''$$$''', '''bac''', '''ab$''', которые после сортировки получат номера 3, 4, 0, 2, 1 соответственно.&lt;br /&gt;
# Строкой '''bbacab$$$bacab$$$$''' в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; = 340210. &lt;br /&gt;
# После рекурсивного вызова получим &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [5, 2, 4, 3, 0, 1]. Пересчитав &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, получим [(5 - 3)*3 + 2, 2 * 3 + 1, (4 - 3) * 3 + 2, (3 - 3) * 3 + 2, 0 * 3 + 1, 1 * 3 + 1] = [8, 7, 5, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и выбрав в нем элементы, равные 1 по модулю 3, получим массив пар &amp;lt;tex&amp;gt;M&amp;lt;/tex&amp;gt; = [(&amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, 7), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 1), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 4)]&lt;br /&gt;
# После устойчивой сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''a''', 1), ('''a''', 4), ('''b''', 7)]&lt;br /&gt;
# Восстановив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;, получаем [0, 3, 6].&lt;br /&gt;
* '''Слияние суффиксных массивов'''&lt;br /&gt;
Рассмотрим, к примеру, третью итерацию слияния, к этой итерации массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 2, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0, на ней мы сливаем суффиксы, соответствующие позициям 5 и 0. &lt;br /&gt;
# Образуем тройки &amp;lt;tex&amp;gt;(S^*[5], S^*[6], S^*[7..8])&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;(S^*[0], S^*[1], S^*[2..8])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
# После получения относительного порядка суффиксов, получим тройки ('''a''', '''b''', 1) и ('''a''', '''b''', 3). Первая тройка меньше второй, поэтому добавляем суффикс, соответствующий позиции 5 в массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# В конце итерации получаем &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 3, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0.&lt;br /&gt;
К концу слияния получим &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5, 0, 3, 6, 2, 1, 4]. Так как мы добавляли один символ &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; в начале алгоритма для дополнения строки до длины, кратной трем, выбросим последний суффикс из &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;, получим в итоге, что &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [7, 5, 0, 3, 6, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; acab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; b\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; cab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
Заметим, что № 9 будет выброшен, так как в начале алгоритма был добавлен один &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt; к строке&lt;br /&gt;
 &lt;br /&gt;
{|&lt;br /&gt;
| [[Файл:Kark_sanders_stage1.png|325px|thumb| Фаза 1]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage2.png|325px|thumb| Фаза 2]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage3.png|325px|thumb| Фаза 3]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Получение массива LCP ==&lt;br /&gt;
Массив LCP можно получить за линейное время [[Алгоритм_Касаи_и_др. | алгоритмом Касаи]].&lt;br /&gt;
&lt;br /&gt;
== Обобщение алгоритма ==&lt;br /&gt;
На самом деле, алгоритм можно обобщить&amp;lt;ref name=&amp;quot;generalisation&amp;quot;&amp;gt; Juha Kärkkäinen, Peter Sanders and Stefan Burkhardt. Linear work suffix array construction. http://www.cs.helsinki.fi/juha.karkkainen/publications/jacm05-revised.pdf &amp;lt;/ref&amp;gt;, взяв на первом шаге, к примеру, суффиксы, позиции которых по модулю 7 дают 3, 5 и 6. Для этого потребуются некоторое усложнение алгоритма, например, сортировка оставшихся суффиксов в нескольких группах на шаге 2 и слияние нескольких групп на шаге 3, но основная идея алгоритма остается той же. Множества, которые можно выбрать, на первом шаге определяются '''разностным покрытием''' (''difference cover'').&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Разностное покрытие''' (''difference cover'') &amp;lt;tex&amp;gt; D &amp;lt;/tex&amp;gt; по модулю &amp;lt;tex&amp;gt;m &amp;lt;/tex&amp;gt; — множество чисел от &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;m - 1 &amp;lt;/tex&amp;gt; таких, что &amp;lt;tex&amp;gt; \forall i \in [0, m-1]: \exists j, k \in D: i \equiv k - j \pmod m &amp;lt;/tex&amp;gt;.&lt;br /&gt;
}} &lt;br /&gt;
Например, &amp;lt;tex&amp;gt; \{1, 2\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt; \{3, 5, 6\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt; \{1\} &amp;lt;/tex&amp;gt; — не является разностным покрытием по модулю &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;, поэтому этот алгоритм не применим к нему. Подробнее узнать, как вычислять разностное покрытие для заданного модуля можно также здесь&amp;lt;ref name=&amp;quot;generalisation&amp;quot;/&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Источники информации==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
* [http://www.cs.helsinki.fi/juha.karkkainen/publications/icalp03.pdf Juha Kärkkäinen and Peter Sanders. Simple linear work suffix array construction]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Суффиксный массив]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=48198</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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=48198"/>
				<updated>2015-06-09T06:16:47Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Суффиксный массив для нечетных суффиксов */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Алгоритм Карккайнена-Сандерса''' (Kärkkäinen, Sanders) — алгоритм построения [[суффиксный массив | суффиксного массива]] за линейное время.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Используемые обозначения ==&lt;br /&gt;
* В данном конспекте используется 0-индексация.&lt;br /&gt;
* &amp;lt;tex&amp;gt;S[i..j] &amp;lt;/tex&amp;gt; — подстрока строки &amp;lt;tex&amp;gt; S &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;-й символы включительно.&lt;br /&gt;
* Пусть длина строки &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;. Обозначим &amp;lt;tex&amp;gt; S^*[i..j] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; j \geqslant n &amp;lt;/tex&amp;gt; как строку &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt;, дополненную защитными символами &amp;lt;tex&amp;gt; \$ &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;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Четным суффиксом''' назовем суффикс, начинающийся в четной позиции. &amp;lt;br&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;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left( \dfrac{n}{2}\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для упрощения алгоритма вначале дополним нашу строку до четной длины (например, добавлением &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; в конец). На шаге слияния мы сможем избавиться от него.&lt;br /&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;
&lt;br /&gt;
# Отобразим исходную строку &amp;lt;tex&amp;gt; S^* &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; длины  &amp;lt;tex&amp;gt; \dfrac{n}{2} &amp;lt;/tex&amp;gt;  следующим образом:&lt;br /&gt;
#* Сделаем список, состоящий из пар символов вида &amp;lt;tex&amp;gt; S^*[i..i + 1] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; i \bmod 2 = 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его цифровой сортировкой за линейное время и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S^*[1..n] &amp;lt;/tex&amp;gt; в алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;, получив строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; половинной длины.&lt;br /&gt;
# Рекурсивно построим суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Построим суффиксный массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;. Очевидно, &amp;lt;tex&amp;gt; A_{S_o}[i] = 2 A_{S'}[i] + 1 &amp;lt;/tex&amp;gt;, так отношение упорядоченности любых двух строк в старом алфавите &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt; эквивалентно отношению упорядоченности в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; по его построению.&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для четных суффиксов=====&lt;br /&gt;
На этом шаге мы за линейное время получим суффиксный массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; для четных суффиксов, используя уже построенный &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что сортировка множества четных суффиксов &amp;lt;tex&amp;gt; \{ S^*[i..n] \mid i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке множества пар &amp;lt;tex&amp;gt; \{ (S^*[i], S^*[i+1..n]) \mid i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt;. Однако &amp;lt;tex&amp;gt; S^*[i+1..n] &amp;lt;/tex&amp;gt; — нечетный суффикс, и его относительную позицию мы уже узнали на шаге 1.&lt;br /&gt;
&lt;br /&gt;
Таким образом, чтобы отсортировать эти пары за линейное время, сначала сразу выпишем их в порядке возрастания второго элемента пары (то есть в порядке вхождения в массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;), а потом отсортируем устойчивой сортировкой подсчетом по первым элементам. После этого легко можно восстановить массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
     M.add(Pair(S[&amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
    &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&gt;
&lt;br /&gt;
Заметим, что массив &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; перед сортировкой подсчетом не был явно отсортирован по вторым элементам, и хранил не суффиксы, а их позиции в строке &amp;lt;tex&amp;gt; S &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;
Для суффиксного дерева третий шаг алгоритма опирается на специфические особенности суффиксных деревьев, которые не присущи суффиксным массивам&amp;lt;ref&amp;gt;M. Farach. Optimal suffix tree construction with large alphabets. http://www.cs.rutgers.edu/~farach/pubs/FarFerrMuthu00.pdf &amp;lt;/ref&amp;gt; .  &lt;br /&gt;
В случае суффиксного массива слияние становится очень сложным, но все же оно было реализовано в алгоритме Ким-Сим-Парк-Парка&amp;lt;ref&amp;gt; D. K. Kim, J. S. Sim, H. Park, and K. Park. Linear-time construction of suffix arrays. http://www.springerlink.com/content/568156021q45r320/&amp;lt;/ref&amp;gt;. Однако простой модификацией алгоритма можно значительно упростить его.&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
Покажем первые два шага агоритма для строки '''ababbbaa'''.&lt;br /&gt;
&lt;br /&gt;
Во-первых, добавив защитный символ '''$''', получив строку '''ababbbaa$''' (для этого алгоритма он не требуется, но может понадобиться в применениях суффиксного массива). Во-вторых, дополним ее до четной длины, получив '''ababbbaa$$'''.&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для нечетных суффиксов=====&lt;br /&gt;
# В новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет четыре элемента — '''ba''', '''bb''', '''a$''', '''$$'''. После сортировки они получат номера 2, 3, 1 и 0 соответственно.&lt;br /&gt;
# Переводим строку &amp;lt;tex&amp;gt;S^*[1..n]&amp;lt;/tex&amp;gt; = '''babbbaa$$$''' в новый алфавит. Сжатой строкой &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; будет '''23210'''.&lt;br /&gt;
# После рекурсивного вызова получим, что &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [4, 3, 2, 0, 1], и &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; = [9, 7, 5, 1, 3].&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для четных суффиксов=====&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''$''', 9), ('''a''', 7), ('''b''', 5), ('''a''', 1), ('''a''', 3)].&lt;br /&gt;
# После сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt;= [('''$''', 9), ('''a''', 7), ('''a''', 1), ('''a''', 3), ('''b''', 5)].&lt;br /&gt;
# Восстановив массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;, получаем [8, 6, 0, 2, 4], что действительно является суффиксным массивом для четных суффиксов.&lt;br /&gt;
&lt;br /&gt;
=====Слияние суффиксных массивов=====&lt;br /&gt;
Если бы мы умели сливать &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; за линейное время, получили бы:&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 9 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; a\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; aa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ababbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; baa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; babbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Как и было сказано вначале, избавиться от лишних '''$''' легко, так как суффиксы, им соответствующие, будут первыми в суффиксном массиве (в данном случае достаточно выбросить &amp;quot;9&amp;quot; из суффиксного массива).&lt;br /&gt;
&lt;br /&gt;
== Алгоритм Карккайнена-Сандерса == &lt;br /&gt;
Изменим изначальный алгоритм следующим образом:&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих позициям, не кратным трем. Рекурсивно сведем это к построению суффиксного массива для строки длиной в две трети исходной.&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих кратным трем позициям, используя результат первого шага, за линейное время.&lt;br /&gt;
# Сольем эти суффиксные массивы в один за линейное время.&lt;br /&gt;
&lt;br /&gt;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left(\dfrac23 n\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого также является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt; (это видно из того, что сумма геометрической прогрессии с основанием &amp;lt;tex&amp;gt; \dfrac23 &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Аналогично первой версии алгоритма, дополним строку &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; до длины, кратной трем, защитными символами &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* '''База рекурсии'''&lt;br /&gt;
Для этого алгоритма минимальной базой рекурсии будет строка длиной 4, так как она дополняется до длины 6, после чего вновь следует рекурсивный вызов строки длиной 4, и, если бы база была меньше 4, алгоритм вошел бы в бесконечную рекурсию. На этом этапе также можно применить обычную сортировку суффиксов, так как это потребует &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
На этом шаге строится суффиксный массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; для множества суффиксов &amp;lt;tex&amp;gt; \{ S[i..n-1] \mid i \bmod 3 \ne 0 \} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Получим строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; аналогично предыдущему алгоритму:&lt;br /&gt;
#* Сделаем список, состоящий из троек &amp;lt;tex&amp;gt; S[i..i+2]&amp;lt;/tex&amp;gt; , где &amp;lt;tex&amp;gt; i \bmod 3 \ne 0 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его за линейное время цифровой сортировкой и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S[1..n]S[2..n+1] &amp;lt;/tex&amp;gt; в строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; длиной &amp;lt;tex&amp;gt; \dfrac23 n &amp;lt;/tex&amp;gt; в алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;. Тогда суффиксу &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt; в старом алфавите, где &amp;lt;tex&amp;gt; i \bmod 3 = 1 &amp;lt;/tex&amp;gt;, в новом алфавите будет соответствовать строка &amp;lt;tex&amp;gt; S'\left[\dfrac{i-1}{3}..\dfrac{n}{3} - 1\right] &amp;lt;/tex&amp;gt;, а если &amp;lt;tex&amp;gt; i\bmod 3 = 2 &amp;lt;/tex&amp;gt;, то строка &amp;lt;tex&amp;gt; S'\left[\dfrac{n}{3} + \dfrac{i-2}{3}..\dfrac{2n}{3} - 1\right] &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Вызовем алгоритм рекурсивно для строки &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt;, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Пройдем по массиву &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt; A_{S'}[i] &amp;lt; \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j = 3A_{S'}[i] + 1 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;, если же &amp;lt;tex&amp;gt; A_{S'}[i] \geqslant \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j =  3\left(A_{S'}[i] - \dfrac{n}{3}\right) + 2 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;. Псевдокод получения &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;.length - 1:&lt;br /&gt;
    '''if''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] &amp;lt; n/3:&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] + 1)&lt;br /&gt;
    '''else''':&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * (&amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] - n/3) + 2)&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
Этот шаг также аналогичен первой версии алгоритма. Сортировка множества &amp;lt;tex&amp;gt; \{ S[i..n-1] \mid i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке пар &amp;lt;tex&amp;gt; \{ (S[i], S[i+1..n-1]) \mid i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; S[i+1..n-1] &amp;lt;/tex&amp;gt; — суффиксы в позициях, равных 1 по модулю 3, относительный порядок которых уже известен. Выпишем эти пары в порядке вхождения их в &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и отсортируем по первому элементу устойчивой сортировкой подсчетом, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;. Псевдокод этого шага:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' 2n/3 - 1:&lt;br /&gt;
     '''if''' &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[i] % 3 == 1:&lt;br /&gt;
         M.add(Pair(S[&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 '''for''' i = 0 '''to''' n/3 - 1:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&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; A_{S_0} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, чтобы получить суффиксный массив &amp;lt;tex&amp;gt; A_{S} &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;
&lt;br /&gt;
Пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 1 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Это аналогично сравнению пар &amp;lt;tex&amp;gt; (S[i], S[i+1..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S[j], S[j+1..n-1]) &amp;lt;/tex&amp;gt;. Сравнить первые элементы пар мы можем за &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt;, а относительный порядок вторых элементов пар нам уже известен, так как они соответствуют позициям, равным 2 и 1 по модулю 3 соответственно.&lt;br /&gt;
&lt;br /&gt;
Аналогично, пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 2 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Тогда это аналогично сравнению троек &amp;lt;tex&amp;gt; (S[i], S[i+1], S[i+2..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S[j], S[j+1], S[j+2..n-1]) &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;A_{S}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 // Вначале предподсчитаем за O(n) обратную перестановку для суффиксного массива &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;, то есть массив rank такой, что &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[rank[i]] = i.&lt;br /&gt;
 // Тогда мы сможем за O(1) сравнивать суффиксы по их позиции.&lt;br /&gt;
 rank = inverse(&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;) &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3 '''and''' j &amp;lt; n/3:&lt;br /&gt;
     pos12 = &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i]&lt;br /&gt;
     pos0  = &amp;lt;tex&amp;gt; A_{0} &amp;lt;/tex&amp;gt;[j]&lt;br /&gt;
     '''if''' pos12 % 3 == 1:&lt;br /&gt;
        ''' if''' Pair(S[pos12], rank[pos12 + 1]) &amp;lt; Pair(S[pos0], rank[pos0 + 1]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++  &lt;br /&gt;
     '''else''':&lt;br /&gt;
         '''if''' Triple(S[pos12], S[pos12 + 1], rank[pos12 + 2]) &amp;lt; Triple(S[pos0], S[pos0 + 1], rank[pos0 + 2]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++ &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i])&lt;br /&gt;
     i++&lt;br /&gt;
 '''while''' j &amp;lt; n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{0}} &amp;lt;/tex&amp;gt;[j])&lt;br /&gt;
     j++&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;
Построим суффиксный массив для строки '''abbacab'''. После добавления защитного символа и дополнения до кратной трем длины, получим '''abbacab$$'''.&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
# Тройками, соответствующими равными 1 по модулю 3 позициям, будут: '''bba''', '''cab''', '''$$$''', соответствующими равным 2 по модулю 3 — '''bac''', '''cab$''', '''$$$'''. Новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет содержать элементы '''bba''', '''cab''', '''$$$''', '''bac''', '''ab$''', которые после сортировки получат номера 3, 4, 0, 2, 1 соответственно.&lt;br /&gt;
# Строкой '''bbacab$$$bacab$$$$''' в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; = 340210. &lt;br /&gt;
# После рекурсивного вызова получим &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [5, 2, 4, 3, 0, 1]. Пересчитав &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, получим [(5 - 3)*3 + 2, 2 * 3 + 1, (4 - 3) * 3 + 2, (3 - 3) * 3 + 2, 0 * 3 + 1, 1 * 3 + 1] = [8, 7, 5, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и выбрав в нем элементы, равные 1 по модулю 3, получим массив пар &amp;lt;tex&amp;gt;M&amp;lt;/tex&amp;gt; = [(&amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, 7), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 1), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 4)]&lt;br /&gt;
# После устойчивой сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''a''', 1), ('''a''', 4), ('''b''', 7)]&lt;br /&gt;
# Восстановив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;, получаем [0, 3, 6].&lt;br /&gt;
* '''Слияние суффиксных массивов'''&lt;br /&gt;
Рассмотрим, к примеру, третью итерацию слияния, к этой итерации массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 2, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0, на ней мы сливаем суффиксы, соответствующие позициям 5 и 0. &lt;br /&gt;
# Образуем тройки (S[5], S[6], S[7..8]) и (S[0], S[1], S[2..8]).&lt;br /&gt;
# После получения относительного порядка суффиксов, получим тройки ('''a''', '''b''', 1) и ('''a''', '''b''', 3). Первая тройка меньше второй, поэтому добавляем суффикс, соответствующий позиции 5 в массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# В конце итерации получаем &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 3, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0.&lt;br /&gt;
К концу слияния получим &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5, 0, 3, 6, 2, 1, 4]. Так как мы добавляли один символ &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; в начале алгоритма для дополнения строки до длины, кратной трем, выбросим последний суффикс из &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;, получим в итоге, что &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [7, 5, 0, 3, 6, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; acab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; b\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; cab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
Заметим, что № 9 будет выброшен, так как в начале алгоритма был добавлен один &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt; к строке&lt;br /&gt;
 &lt;br /&gt;
{|&lt;br /&gt;
| [[Файл:Kark_sanders_stage1.png|325px|thumb| Фаза 1]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage2.png|325px|thumb| Фаза 2]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage3.png|325px|thumb| Фаза 3]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Получение массива LCP ==&lt;br /&gt;
Массив LCP можно получить за линейное время [[Алгоритм_Касаи_и_др. | алгоритмом Касаи]].&lt;br /&gt;
&lt;br /&gt;
== Обобщение алгоритма ==&lt;br /&gt;
На самом деле, алгоритм можно обобщить&amp;lt;ref name=&amp;quot;generalisation&amp;quot;&amp;gt; Juha Kärkkäinen, Peter Sanders and Stefan Burkhardt. Linear work suffix array construction. http://www.cs.helsinki.fi/juha.karkkainen/publications/jacm05-revised.pdf &amp;lt;/ref&amp;gt;, взяв на первом шаге, к примеру, суффиксы, позиции которых по модулю 7 дают 3, 5 и 6. Для этого потребуются некоторое усложнение алгоритма, например, сортировка оставшихся суффиксов в нескольких группах на шаге 2 и слияние нескольких групп на шаге 3, но основная идея алгоритма остается той же. Множества, которые можно выбрать, на первом шаге определяются '''разностным покрытием''' (''difference cover'').&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Разностное покрытие''' (''difference cover'') &amp;lt;tex&amp;gt; D &amp;lt;/tex&amp;gt; по модулю &amp;lt;tex&amp;gt;m &amp;lt;/tex&amp;gt; — множество чисел от &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;m - 1 &amp;lt;/tex&amp;gt; таких, что &amp;lt;tex&amp;gt; \forall i \in [0, m-1]: \exists j, k \in D: i \equiv k - j \pmod m &amp;lt;/tex&amp;gt;.&lt;br /&gt;
}} &lt;br /&gt;
Например, &amp;lt;tex&amp;gt; \{1, 2\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt; \{3, 5, 6\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt; \{1\} &amp;lt;/tex&amp;gt; — не является разностным покрытием по модулю &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;, поэтому этот алгоритм не применим к нему. Подробнее узнать, как вычислять разностное покрытие для заданного модуля можно также здесь&amp;lt;ref name=&amp;quot;generalisation&amp;quot;/&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Источники информации==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
* [http://www.cs.helsinki.fi/juha.karkkainen/publications/icalp03.pdf Juha Kärkkäinen and Peter Sanders. Simple linear work suffix array construction]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Суффиксный массив]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=48197</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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=48197"/>
				<updated>2015-06-09T06:15:22Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Суффиксный массив для четных суффиксов */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Алгоритм Карккайнена-Сандерса''' (Kärkkäinen, Sanders) — алгоритм построения [[суффиксный массив | суффиксного массива]] за линейное время.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Используемые обозначения ==&lt;br /&gt;
* В данном конспекте используется 0-индексация.&lt;br /&gt;
* &amp;lt;tex&amp;gt;S[i..j] &amp;lt;/tex&amp;gt; — подстрока строки &amp;lt;tex&amp;gt; S &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;-й символы включительно.&lt;br /&gt;
* Пусть длина строки &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;. Обозначим &amp;lt;tex&amp;gt; S^*[i..j] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; j \geqslant n &amp;lt;/tex&amp;gt; как строку &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt;, дополненную защитными символами &amp;lt;tex&amp;gt; \$ &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;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Четным суффиксом''' назовем суффикс, начинающийся в четной позиции. &amp;lt;br&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;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left( \dfrac{n}{2}\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для упрощения алгоритма вначале дополним нашу строку до четной длины (например, добавлением &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; в конец). На шаге слияния мы сможем избавиться от него.&lt;br /&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;
&lt;br /&gt;
# Отобразим исходную строку &amp;lt;tex&amp;gt; S^* &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; длины  &amp;lt;tex&amp;gt; \dfrac{n}{2} &amp;lt;/tex&amp;gt;  следующим образом:&lt;br /&gt;
#* Сделаем список, состоящий из пар символов вида &amp;lt;tex&amp;gt; S^*[i..i + 1] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; i \bmod 2 = 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его цифровой сортировкой за линейное время и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S^*[1..n] &amp;lt;/tex&amp;gt; в алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;, получив строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; половинной длины.&lt;br /&gt;
# Рекурсивно построим суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Построим суффиксный массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;. Очевидно, &amp;lt;tex&amp;gt; A_{S_o}[i] = 2 A_{S'}[i] + 1 &amp;lt;/tex&amp;gt;, так отношение упорядоченности любых двух строк в старом алфавите &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt; эквивалентно отношению упорядоченности в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; по его построению.&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для четных суффиксов=====&lt;br /&gt;
На этом шаге мы за линейное время получим суффиксный массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; для четных суффиксов, используя уже построенный &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что сортировка множества четных суффиксов &amp;lt;tex&amp;gt; \{ S^*[i..n] \mid i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке множества пар &amp;lt;tex&amp;gt; \{ (S^*[i], S^*[i+1..n]) \mid i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt;. Однако &amp;lt;tex&amp;gt; S^*[i+1..n] &amp;lt;/tex&amp;gt; — нечетный суффикс, и его относительную позицию мы уже узнали на шаге 1.&lt;br /&gt;
&lt;br /&gt;
Таким образом, чтобы отсортировать эти пары за линейное время, сначала сразу выпишем их в порядке возрастания второго элемента пары (то есть в порядке вхождения в массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;), а потом отсортируем устойчивой сортировкой подсчетом по первым элементам. После этого легко можно восстановить массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
     M.add(Pair(S[&amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
    &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&gt;
&lt;br /&gt;
Заметим, что массив &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; перед сортировкой подсчетом не был явно отсортирован по вторым элементам, и хранил не суффиксы, а их позиции в строке &amp;lt;tex&amp;gt; S &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;
Для суффиксного дерева третий шаг алгоритма опирается на специфические особенности суффиксных деревьев, которые не присущи суффиксным массивам&amp;lt;ref&amp;gt;M. Farach. Optimal suffix tree construction with large alphabets. http://www.cs.rutgers.edu/~farach/pubs/FarFerrMuthu00.pdf &amp;lt;/ref&amp;gt; .  &lt;br /&gt;
В случае суффиксного массива слияние становится очень сложным, но все же оно было реализовано в алгоритме Ким-Сим-Парк-Парка&amp;lt;ref&amp;gt; D. K. Kim, J. S. Sim, H. Park, and K. Park. Linear-time construction of suffix arrays. http://www.springerlink.com/content/568156021q45r320/&amp;lt;/ref&amp;gt;. Однако простой модификацией алгоритма можно значительно упростить его.&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
Покажем первые два шага агоритма для строки '''ababbbaa'''.&lt;br /&gt;
&lt;br /&gt;
Во-первых, добавив защитный символ '''$''', получив строку '''ababbbaa$''' (для этого алгоритма он не требуется, но может понадобиться в применениях суффиксного массива). Во-вторых, дополним ее до четной длины, получив '''ababbbaa$$'''.&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для нечетных суффиксов=====&lt;br /&gt;
# В новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет четыре элемента — '''ba''', '''bb''', '''a$''', '''$$'''. После сортировки они получат номера 2, 3, 1 и 0 соответственно.&lt;br /&gt;
# Переводим строку S[1..n] = '''babbbaa$$$''' в новый алфавит. Сжатой строкой &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; будет '''23210'''.&lt;br /&gt;
# После рекурсивного вызова получим, что &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [4, 3, 2, 0, 1], и &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; = [9, 7, 5, 1, 3].&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для четных суффиксов=====&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''$''', 9), ('''a''', 7), ('''b''', 5), ('''a''', 1), ('''a''', 3)].&lt;br /&gt;
# После сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt;= [('''$''', 9), ('''a''', 7), ('''a''', 1), ('''a''', 3), ('''b''', 5)].&lt;br /&gt;
# Восстановив массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;, получаем [8, 6, 0, 2, 4], что действительно является суффиксным массивом для четных суффиксов.&lt;br /&gt;
&lt;br /&gt;
=====Слияние суффиксных массивов=====&lt;br /&gt;
Если бы мы умели сливать &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; за линейное время, получили бы:&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 9 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; a\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; aa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ababbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; baa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; babbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Как и было сказано вначале, избавиться от лишних '''$''' легко, так как суффиксы, им соответствующие, будут первыми в суффиксном массиве (в данном случае достаточно выбросить &amp;quot;9&amp;quot; из суффиксного массива).&lt;br /&gt;
&lt;br /&gt;
== Алгоритм Карккайнена-Сандерса == &lt;br /&gt;
Изменим изначальный алгоритм следующим образом:&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих позициям, не кратным трем. Рекурсивно сведем это к построению суффиксного массива для строки длиной в две трети исходной.&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих кратным трем позициям, используя результат первого шага, за линейное время.&lt;br /&gt;
# Сольем эти суффиксные массивы в один за линейное время.&lt;br /&gt;
&lt;br /&gt;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left(\dfrac23 n\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого также является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt; (это видно из того, что сумма геометрической прогрессии с основанием &amp;lt;tex&amp;gt; \dfrac23 &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Аналогично первой версии алгоритма, дополним строку &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; до длины, кратной трем, защитными символами &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* '''База рекурсии'''&lt;br /&gt;
Для этого алгоритма минимальной базой рекурсии будет строка длиной 4, так как она дополняется до длины 6, после чего вновь следует рекурсивный вызов строки длиной 4, и, если бы база была меньше 4, алгоритм вошел бы в бесконечную рекурсию. На этом этапе также можно применить обычную сортировку суффиксов, так как это потребует &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
На этом шаге строится суффиксный массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; для множества суффиксов &amp;lt;tex&amp;gt; \{ S[i..n-1] \mid i \bmod 3 \ne 0 \} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Получим строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; аналогично предыдущему алгоритму:&lt;br /&gt;
#* Сделаем список, состоящий из троек &amp;lt;tex&amp;gt; S[i..i+2]&amp;lt;/tex&amp;gt; , где &amp;lt;tex&amp;gt; i \bmod 3 \ne 0 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его за линейное время цифровой сортировкой и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S[1..n]S[2..n+1] &amp;lt;/tex&amp;gt; в строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; длиной &amp;lt;tex&amp;gt; \dfrac23 n &amp;lt;/tex&amp;gt; в алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;. Тогда суффиксу &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt; в старом алфавите, где &amp;lt;tex&amp;gt; i \bmod 3 = 1 &amp;lt;/tex&amp;gt;, в новом алфавите будет соответствовать строка &amp;lt;tex&amp;gt; S'\left[\dfrac{i-1}{3}..\dfrac{n}{3} - 1\right] &amp;lt;/tex&amp;gt;, а если &amp;lt;tex&amp;gt; i\bmod 3 = 2 &amp;lt;/tex&amp;gt;, то строка &amp;lt;tex&amp;gt; S'\left[\dfrac{n}{3} + \dfrac{i-2}{3}..\dfrac{2n}{3} - 1\right] &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Вызовем алгоритм рекурсивно для строки &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt;, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Пройдем по массиву &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt; A_{S'}[i] &amp;lt; \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j = 3A_{S'}[i] + 1 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;, если же &amp;lt;tex&amp;gt; A_{S'}[i] \geqslant \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j =  3\left(A_{S'}[i] - \dfrac{n}{3}\right) + 2 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;. Псевдокод получения &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;.length - 1:&lt;br /&gt;
    '''if''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] &amp;lt; n/3:&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] + 1)&lt;br /&gt;
    '''else''':&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * (&amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] - n/3) + 2)&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
Этот шаг также аналогичен первой версии алгоритма. Сортировка множества &amp;lt;tex&amp;gt; \{ S[i..n-1] \mid i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке пар &amp;lt;tex&amp;gt; \{ (S[i], S[i+1..n-1]) \mid i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; S[i+1..n-1] &amp;lt;/tex&amp;gt; — суффиксы в позициях, равных 1 по модулю 3, относительный порядок которых уже известен. Выпишем эти пары в порядке вхождения их в &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и отсортируем по первому элементу устойчивой сортировкой подсчетом, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;. Псевдокод этого шага:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' 2n/3 - 1:&lt;br /&gt;
     '''if''' &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[i] % 3 == 1:&lt;br /&gt;
         M.add(Pair(S[&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 '''for''' i = 0 '''to''' n/3 - 1:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&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; A_{S_0} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, чтобы получить суффиксный массив &amp;lt;tex&amp;gt; A_{S} &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;
&lt;br /&gt;
Пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 1 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Это аналогично сравнению пар &amp;lt;tex&amp;gt; (S[i], S[i+1..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S[j], S[j+1..n-1]) &amp;lt;/tex&amp;gt;. Сравнить первые элементы пар мы можем за &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt;, а относительный порядок вторых элементов пар нам уже известен, так как они соответствуют позициям, равным 2 и 1 по модулю 3 соответственно.&lt;br /&gt;
&lt;br /&gt;
Аналогично, пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 2 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Тогда это аналогично сравнению троек &amp;lt;tex&amp;gt; (S[i], S[i+1], S[i+2..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S[j], S[j+1], S[j+2..n-1]) &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;A_{S}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 // Вначале предподсчитаем за O(n) обратную перестановку для суффиксного массива &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;, то есть массив rank такой, что &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[rank[i]] = i.&lt;br /&gt;
 // Тогда мы сможем за O(1) сравнивать суффиксы по их позиции.&lt;br /&gt;
 rank = inverse(&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;) &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3 '''and''' j &amp;lt; n/3:&lt;br /&gt;
     pos12 = &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i]&lt;br /&gt;
     pos0  = &amp;lt;tex&amp;gt; A_{0} &amp;lt;/tex&amp;gt;[j]&lt;br /&gt;
     '''if''' pos12 % 3 == 1:&lt;br /&gt;
        ''' if''' Pair(S[pos12], rank[pos12 + 1]) &amp;lt; Pair(S[pos0], rank[pos0 + 1]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++  &lt;br /&gt;
     '''else''':&lt;br /&gt;
         '''if''' Triple(S[pos12], S[pos12 + 1], rank[pos12 + 2]) &amp;lt; Triple(S[pos0], S[pos0 + 1], rank[pos0 + 2]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++ &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i])&lt;br /&gt;
     i++&lt;br /&gt;
 '''while''' j &amp;lt; n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{0}} &amp;lt;/tex&amp;gt;[j])&lt;br /&gt;
     j++&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;
Построим суффиксный массив для строки '''abbacab'''. После добавления защитного символа и дополнения до кратной трем длины, получим '''abbacab$$'''.&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
# Тройками, соответствующими равными 1 по модулю 3 позициям, будут: '''bba''', '''cab''', '''$$$''', соответствующими равным 2 по модулю 3 — '''bac''', '''cab$''', '''$$$'''. Новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет содержать элементы '''bba''', '''cab''', '''$$$''', '''bac''', '''ab$''', которые после сортировки получат номера 3, 4, 0, 2, 1 соответственно.&lt;br /&gt;
# Строкой '''bbacab$$$bacab$$$$''' в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; = 340210. &lt;br /&gt;
# После рекурсивного вызова получим &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [5, 2, 4, 3, 0, 1]. Пересчитав &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, получим [(5 - 3)*3 + 2, 2 * 3 + 1, (4 - 3) * 3 + 2, (3 - 3) * 3 + 2, 0 * 3 + 1, 1 * 3 + 1] = [8, 7, 5, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и выбрав в нем элементы, равные 1 по модулю 3, получим массив пар &amp;lt;tex&amp;gt;M&amp;lt;/tex&amp;gt; = [(&amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, 7), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 1), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 4)]&lt;br /&gt;
# После устойчивой сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''a''', 1), ('''a''', 4), ('''b''', 7)]&lt;br /&gt;
# Восстановив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;, получаем [0, 3, 6].&lt;br /&gt;
* '''Слияние суффиксных массивов'''&lt;br /&gt;
Рассмотрим, к примеру, третью итерацию слияния, к этой итерации массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 2, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0, на ней мы сливаем суффиксы, соответствующие позициям 5 и 0. &lt;br /&gt;
# Образуем тройки (S[5], S[6], S[7..8]) и (S[0], S[1], S[2..8]).&lt;br /&gt;
# После получения относительного порядка суффиксов, получим тройки ('''a''', '''b''', 1) и ('''a''', '''b''', 3). Первая тройка меньше второй, поэтому добавляем суффикс, соответствующий позиции 5 в массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# В конце итерации получаем &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 3, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0.&lt;br /&gt;
К концу слияния получим &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5, 0, 3, 6, 2, 1, 4]. Так как мы добавляли один символ &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; в начале алгоритма для дополнения строки до длины, кратной трем, выбросим последний суффикс из &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;, получим в итоге, что &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [7, 5, 0, 3, 6, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; acab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; b\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; cab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
Заметим, что № 9 будет выброшен, так как в начале алгоритма был добавлен один &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt; к строке&lt;br /&gt;
 &lt;br /&gt;
{|&lt;br /&gt;
| [[Файл:Kark_sanders_stage1.png|325px|thumb| Фаза 1]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage2.png|325px|thumb| Фаза 2]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage3.png|325px|thumb| Фаза 3]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Получение массива LCP ==&lt;br /&gt;
Массив LCP можно получить за линейное время [[Алгоритм_Касаи_и_др. | алгоритмом Касаи]].&lt;br /&gt;
&lt;br /&gt;
== Обобщение алгоритма ==&lt;br /&gt;
На самом деле, алгоритм можно обобщить&amp;lt;ref name=&amp;quot;generalisation&amp;quot;&amp;gt; Juha Kärkkäinen, Peter Sanders and Stefan Burkhardt. Linear work suffix array construction. http://www.cs.helsinki.fi/juha.karkkainen/publications/jacm05-revised.pdf &amp;lt;/ref&amp;gt;, взяв на первом шаге, к примеру, суффиксы, позиции которых по модулю 7 дают 3, 5 и 6. Для этого потребуются некоторое усложнение алгоритма, например, сортировка оставшихся суффиксов в нескольких группах на шаге 2 и слияние нескольких групп на шаге 3, но основная идея алгоритма остается той же. Множества, которые можно выбрать, на первом шаге определяются '''разностным покрытием''' (''difference cover'').&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Разностное покрытие''' (''difference cover'') &amp;lt;tex&amp;gt; D &amp;lt;/tex&amp;gt; по модулю &amp;lt;tex&amp;gt;m &amp;lt;/tex&amp;gt; — множество чисел от &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;m - 1 &amp;lt;/tex&amp;gt; таких, что &amp;lt;tex&amp;gt; \forall i \in [0, m-1]: \exists j, k \in D: i \equiv k - j \pmod m &amp;lt;/tex&amp;gt;.&lt;br /&gt;
}} &lt;br /&gt;
Например, &amp;lt;tex&amp;gt; \{1, 2\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt; \{3, 5, 6\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt; \{1\} &amp;lt;/tex&amp;gt; — не является разностным покрытием по модулю &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;, поэтому этот алгоритм не применим к нему. Подробнее узнать, как вычислять разностное покрытие для заданного модуля можно также здесь&amp;lt;ref name=&amp;quot;generalisation&amp;quot;/&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Источники информации==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
* [http://www.cs.helsinki.fi/juha.karkkainen/publications/icalp03.pdf Juha Kärkkäinen and Peter Sanders. Simple linear work suffix array construction]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Суффиксный массив]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=48196</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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=48196"/>
				<updated>2015-06-09T06:13:38Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Суффиксный массив для нечетных суффиксов */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Алгоритм Карккайнена-Сандерса''' (Kärkkäinen, Sanders) — алгоритм построения [[суффиксный массив | суффиксного массива]] за линейное время.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Используемые обозначения ==&lt;br /&gt;
* В данном конспекте используется 0-индексация.&lt;br /&gt;
* &amp;lt;tex&amp;gt;S[i..j] &amp;lt;/tex&amp;gt; — подстрока строки &amp;lt;tex&amp;gt; S &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;-й символы включительно.&lt;br /&gt;
* Пусть длина строки &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;. Обозначим &amp;lt;tex&amp;gt; S^*[i..j] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; j \geqslant n &amp;lt;/tex&amp;gt; как строку &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt;, дополненную защитными символами &amp;lt;tex&amp;gt; \$ &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;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Четным суффиксом''' назовем суффикс, начинающийся в четной позиции. &amp;lt;br&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;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left( \dfrac{n}{2}\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для упрощения алгоритма вначале дополним нашу строку до четной длины (например, добавлением &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; в конец). На шаге слияния мы сможем избавиться от него.&lt;br /&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;
&lt;br /&gt;
# Отобразим исходную строку &amp;lt;tex&amp;gt; S^* &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; длины  &amp;lt;tex&amp;gt; \dfrac{n}{2} &amp;lt;/tex&amp;gt;  следующим образом:&lt;br /&gt;
#* Сделаем список, состоящий из пар символов вида &amp;lt;tex&amp;gt; S^*[i..i + 1] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; i \bmod 2 = 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его цифровой сортировкой за линейное время и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S^*[1..n] &amp;lt;/tex&amp;gt; в алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;, получив строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; половинной длины.&lt;br /&gt;
# Рекурсивно построим суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Построим суффиксный массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;. Очевидно, &amp;lt;tex&amp;gt; A_{S_o}[i] = 2 A_{S'}[i] + 1 &amp;lt;/tex&amp;gt;, так отношение упорядоченности любых двух строк в старом алфавите &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt; эквивалентно отношению упорядоченности в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; по его построению.&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для четных суффиксов=====&lt;br /&gt;
На этом шаге мы за линейное время получим суффиксный массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; для четных суффиксов, используя уже построенный &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что сортировка множества четных суффиксов &amp;lt;tex&amp;gt; \{ S[i..n] \mid i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке множества пар &amp;lt;tex&amp;gt; \{ (S[i], S[i+1..n]) \mid i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt;. Однако &amp;lt;tex&amp;gt; S[i+1..n] &amp;lt;/tex&amp;gt; — нечетный суффикс, и его относительную позицию мы уже узнали на шаге 1.&lt;br /&gt;
&lt;br /&gt;
Таким образом, чтобы отсортировать эти пары за линейное время, сначала сразу выпишем их в порядке возрастания второго элемента пары (то есть в порядке вхождения в массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;), а потом отсортируем устойчивой сортировкой подсчетом по первым элементам. После этого легко можно восстановить массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
     M.add(Pair(S[&amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
    &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&gt;
&lt;br /&gt;
Заметим, что массив &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; перед сортировкой подсчетом не был явно отсортирован по вторым элементам, и хранил не суффиксы, а их позиции в строке &amp;lt;tex&amp;gt; S &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;
Для суффиксного дерева третий шаг алгоритма опирается на специфические особенности суффиксных деревьев, которые не присущи суффиксным массивам&amp;lt;ref&amp;gt;M. Farach. Optimal suffix tree construction with large alphabets. http://www.cs.rutgers.edu/~farach/pubs/FarFerrMuthu00.pdf &amp;lt;/ref&amp;gt; .  &lt;br /&gt;
В случае суффиксного массива слияние становится очень сложным, но все же оно было реализовано в алгоритме Ким-Сим-Парк-Парка&amp;lt;ref&amp;gt; D. K. Kim, J. S. Sim, H. Park, and K. Park. Linear-time construction of suffix arrays. http://www.springerlink.com/content/568156021q45r320/&amp;lt;/ref&amp;gt;. Однако простой модификацией алгоритма можно значительно упростить его.&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
Покажем первые два шага агоритма для строки '''ababbbaa'''.&lt;br /&gt;
&lt;br /&gt;
Во-первых, добавив защитный символ '''$''', получив строку '''ababbbaa$''' (для этого алгоритма он не требуется, но может понадобиться в применениях суффиксного массива). Во-вторых, дополним ее до четной длины, получив '''ababbbaa$$'''.&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для нечетных суффиксов=====&lt;br /&gt;
# В новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет четыре элемента — '''ba''', '''bb''', '''a$''', '''$$'''. После сортировки они получат номера 2, 3, 1 и 0 соответственно.&lt;br /&gt;
# Переводим строку S[1..n] = '''babbbaa$$$''' в новый алфавит. Сжатой строкой &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; будет '''23210'''.&lt;br /&gt;
# После рекурсивного вызова получим, что &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [4, 3, 2, 0, 1], и &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; = [9, 7, 5, 1, 3].&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для четных суффиксов=====&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''$''', 9), ('''a''', 7), ('''b''', 5), ('''a''', 1), ('''a''', 3)].&lt;br /&gt;
# После сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt;= [('''$''', 9), ('''a''', 7), ('''a''', 1), ('''a''', 3), ('''b''', 5)].&lt;br /&gt;
# Восстановив массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;, получаем [8, 6, 0, 2, 4], что действительно является суффиксным массивом для четных суффиксов.&lt;br /&gt;
&lt;br /&gt;
=====Слияние суффиксных массивов=====&lt;br /&gt;
Если бы мы умели сливать &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; за линейное время, получили бы:&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 9 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; a\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; aa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ababbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; baa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; babbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Как и было сказано вначале, избавиться от лишних '''$''' легко, так как суффиксы, им соответствующие, будут первыми в суффиксном массиве (в данном случае достаточно выбросить &amp;quot;9&amp;quot; из суффиксного массива).&lt;br /&gt;
&lt;br /&gt;
== Алгоритм Карккайнена-Сандерса == &lt;br /&gt;
Изменим изначальный алгоритм следующим образом:&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих позициям, не кратным трем. Рекурсивно сведем это к построению суффиксного массива для строки длиной в две трети исходной.&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих кратным трем позициям, используя результат первого шага, за линейное время.&lt;br /&gt;
# Сольем эти суффиксные массивы в один за линейное время.&lt;br /&gt;
&lt;br /&gt;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left(\dfrac23 n\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого также является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt; (это видно из того, что сумма геометрической прогрессии с основанием &amp;lt;tex&amp;gt; \dfrac23 &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Аналогично первой версии алгоритма, дополним строку &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; до длины, кратной трем, защитными символами &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* '''База рекурсии'''&lt;br /&gt;
Для этого алгоритма минимальной базой рекурсии будет строка длиной 4, так как она дополняется до длины 6, после чего вновь следует рекурсивный вызов строки длиной 4, и, если бы база была меньше 4, алгоритм вошел бы в бесконечную рекурсию. На этом этапе также можно применить обычную сортировку суффиксов, так как это потребует &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
На этом шаге строится суффиксный массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; для множества суффиксов &amp;lt;tex&amp;gt; \{ S[i..n-1] \mid i \bmod 3 \ne 0 \} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Получим строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; аналогично предыдущему алгоритму:&lt;br /&gt;
#* Сделаем список, состоящий из троек &amp;lt;tex&amp;gt; S[i..i+2]&amp;lt;/tex&amp;gt; , где &amp;lt;tex&amp;gt; i \bmod 3 \ne 0 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его за линейное время цифровой сортировкой и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S[1..n]S[2..n+1] &amp;lt;/tex&amp;gt; в строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; длиной &amp;lt;tex&amp;gt; \dfrac23 n &amp;lt;/tex&amp;gt; в алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;. Тогда суффиксу &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt; в старом алфавите, где &amp;lt;tex&amp;gt; i \bmod 3 = 1 &amp;lt;/tex&amp;gt;, в новом алфавите будет соответствовать строка &amp;lt;tex&amp;gt; S'\left[\dfrac{i-1}{3}..\dfrac{n}{3} - 1\right] &amp;lt;/tex&amp;gt;, а если &amp;lt;tex&amp;gt; i\bmod 3 = 2 &amp;lt;/tex&amp;gt;, то строка &amp;lt;tex&amp;gt; S'\left[\dfrac{n}{3} + \dfrac{i-2}{3}..\dfrac{2n}{3} - 1\right] &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Вызовем алгоритм рекурсивно для строки &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt;, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Пройдем по массиву &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt; A_{S'}[i] &amp;lt; \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j = 3A_{S'}[i] + 1 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;, если же &amp;lt;tex&amp;gt; A_{S'}[i] \geqslant \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j =  3\left(A_{S'}[i] - \dfrac{n}{3}\right) + 2 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;. Псевдокод получения &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;.length - 1:&lt;br /&gt;
    '''if''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] &amp;lt; n/3:&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] + 1)&lt;br /&gt;
    '''else''':&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * (&amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] - n/3) + 2)&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
Этот шаг также аналогичен первой версии алгоритма. Сортировка множества &amp;lt;tex&amp;gt; \{ S[i..n-1] \mid i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке пар &amp;lt;tex&amp;gt; \{ (S[i], S[i+1..n-1]) \mid i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; S[i+1..n-1] &amp;lt;/tex&amp;gt; — суффиксы в позициях, равных 1 по модулю 3, относительный порядок которых уже известен. Выпишем эти пары в порядке вхождения их в &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и отсортируем по первому элементу устойчивой сортировкой подсчетом, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;. Псевдокод этого шага:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' 2n/3 - 1:&lt;br /&gt;
     '''if''' &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[i] % 3 == 1:&lt;br /&gt;
         M.add(Pair(S[&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 '''for''' i = 0 '''to''' n/3 - 1:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&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; A_{S_0} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, чтобы получить суффиксный массив &amp;lt;tex&amp;gt; A_{S} &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;
&lt;br /&gt;
Пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 1 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Это аналогично сравнению пар &amp;lt;tex&amp;gt; (S[i], S[i+1..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S[j], S[j+1..n-1]) &amp;lt;/tex&amp;gt;. Сравнить первые элементы пар мы можем за &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt;, а относительный порядок вторых элементов пар нам уже известен, так как они соответствуют позициям, равным 2 и 1 по модулю 3 соответственно.&lt;br /&gt;
&lt;br /&gt;
Аналогично, пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 2 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Тогда это аналогично сравнению троек &amp;lt;tex&amp;gt; (S[i], S[i+1], S[i+2..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S[j], S[j+1], S[j+2..n-1]) &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;A_{S}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 // Вначале предподсчитаем за O(n) обратную перестановку для суффиксного массива &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;, то есть массив rank такой, что &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[rank[i]] = i.&lt;br /&gt;
 // Тогда мы сможем за O(1) сравнивать суффиксы по их позиции.&lt;br /&gt;
 rank = inverse(&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;) &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3 '''and''' j &amp;lt; n/3:&lt;br /&gt;
     pos12 = &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i]&lt;br /&gt;
     pos0  = &amp;lt;tex&amp;gt; A_{0} &amp;lt;/tex&amp;gt;[j]&lt;br /&gt;
     '''if''' pos12 % 3 == 1:&lt;br /&gt;
        ''' if''' Pair(S[pos12], rank[pos12 + 1]) &amp;lt; Pair(S[pos0], rank[pos0 + 1]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++  &lt;br /&gt;
     '''else''':&lt;br /&gt;
         '''if''' Triple(S[pos12], S[pos12 + 1], rank[pos12 + 2]) &amp;lt; Triple(S[pos0], S[pos0 + 1], rank[pos0 + 2]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++ &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i])&lt;br /&gt;
     i++&lt;br /&gt;
 '''while''' j &amp;lt; n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{0}} &amp;lt;/tex&amp;gt;[j])&lt;br /&gt;
     j++&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;
Построим суффиксный массив для строки '''abbacab'''. После добавления защитного символа и дополнения до кратной трем длины, получим '''abbacab$$'''.&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
# Тройками, соответствующими равными 1 по модулю 3 позициям, будут: '''bba''', '''cab''', '''$$$''', соответствующими равным 2 по модулю 3 — '''bac''', '''cab$''', '''$$$'''. Новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет содержать элементы '''bba''', '''cab''', '''$$$''', '''bac''', '''ab$''', которые после сортировки получат номера 3, 4, 0, 2, 1 соответственно.&lt;br /&gt;
# Строкой '''bbacab$$$bacab$$$$''' в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; = 340210. &lt;br /&gt;
# После рекурсивного вызова получим &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [5, 2, 4, 3, 0, 1]. Пересчитав &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, получим [(5 - 3)*3 + 2, 2 * 3 + 1, (4 - 3) * 3 + 2, (3 - 3) * 3 + 2, 0 * 3 + 1, 1 * 3 + 1] = [8, 7, 5, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и выбрав в нем элементы, равные 1 по модулю 3, получим массив пар &amp;lt;tex&amp;gt;M&amp;lt;/tex&amp;gt; = [(&amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, 7), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 1), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 4)]&lt;br /&gt;
# После устойчивой сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''a''', 1), ('''a''', 4), ('''b''', 7)]&lt;br /&gt;
# Восстановив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;, получаем [0, 3, 6].&lt;br /&gt;
* '''Слияние суффиксных массивов'''&lt;br /&gt;
Рассмотрим, к примеру, третью итерацию слияния, к этой итерации массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 2, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0, на ней мы сливаем суффиксы, соответствующие позициям 5 и 0. &lt;br /&gt;
# Образуем тройки (S[5], S[6], S[7..8]) и (S[0], S[1], S[2..8]).&lt;br /&gt;
# После получения относительного порядка суффиксов, получим тройки ('''a''', '''b''', 1) и ('''a''', '''b''', 3). Первая тройка меньше второй, поэтому добавляем суффикс, соответствующий позиции 5 в массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# В конце итерации получаем &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 3, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0.&lt;br /&gt;
К концу слияния получим &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5, 0, 3, 6, 2, 1, 4]. Так как мы добавляли один символ &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; в начале алгоритма для дополнения строки до длины, кратной трем, выбросим последний суффикс из &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;, получим в итоге, что &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [7, 5, 0, 3, 6, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; acab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; b\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; cab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
Заметим, что № 9 будет выброшен, так как в начале алгоритма был добавлен один &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt; к строке&lt;br /&gt;
 &lt;br /&gt;
{|&lt;br /&gt;
| [[Файл:Kark_sanders_stage1.png|325px|thumb| Фаза 1]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage2.png|325px|thumb| Фаза 2]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage3.png|325px|thumb| Фаза 3]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Получение массива LCP ==&lt;br /&gt;
Массив LCP можно получить за линейное время [[Алгоритм_Касаи_и_др. | алгоритмом Касаи]].&lt;br /&gt;
&lt;br /&gt;
== Обобщение алгоритма ==&lt;br /&gt;
На самом деле, алгоритм можно обобщить&amp;lt;ref name=&amp;quot;generalisation&amp;quot;&amp;gt; Juha Kärkkäinen, Peter Sanders and Stefan Burkhardt. Linear work suffix array construction. http://www.cs.helsinki.fi/juha.karkkainen/publications/jacm05-revised.pdf &amp;lt;/ref&amp;gt;, взяв на первом шаге, к примеру, суффиксы, позиции которых по модулю 7 дают 3, 5 и 6. Для этого потребуются некоторое усложнение алгоритма, например, сортировка оставшихся суффиксов в нескольких группах на шаге 2 и слияние нескольких групп на шаге 3, но основная идея алгоритма остается той же. Множества, которые можно выбрать, на первом шаге определяются '''разностным покрытием''' (''difference cover'').&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Разностное покрытие''' (''difference cover'') &amp;lt;tex&amp;gt; D &amp;lt;/tex&amp;gt; по модулю &amp;lt;tex&amp;gt;m &amp;lt;/tex&amp;gt; — множество чисел от &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;m - 1 &amp;lt;/tex&amp;gt; таких, что &amp;lt;tex&amp;gt; \forall i \in [0, m-1]: \exists j, k \in D: i \equiv k - j \pmod m &amp;lt;/tex&amp;gt;.&lt;br /&gt;
}} &lt;br /&gt;
Например, &amp;lt;tex&amp;gt; \{1, 2\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt; \{3, 5, 6\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt; \{1\} &amp;lt;/tex&amp;gt; — не является разностным покрытием по модулю &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;, поэтому этот алгоритм не применим к нему. Подробнее узнать, как вычислять разностное покрытие для заданного модуля можно также здесь&amp;lt;ref name=&amp;quot;generalisation&amp;quot;/&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Источники информации==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
* [http://www.cs.helsinki.fi/juha.karkkainen/publications/icalp03.pdf Juha Kärkkäinen and Peter Sanders. Simple linear work suffix array construction]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Суффиксный массив]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=48195</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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=48195"/>
				<updated>2015-06-09T05:58:53Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Алгоритм Карккайнена-Сандерса */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Алгоритм Карккайнена-Сандерса''' (Kärkkäinen, Sanders) — алгоритм построения [[суффиксный массив | суффиксного массива]] за линейное время.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Используемые обозначения ==&lt;br /&gt;
* В данном конспекте используется 0-индексация.&lt;br /&gt;
* &amp;lt;tex&amp;gt;S[i..j] &amp;lt;/tex&amp;gt; — подстрока строки &amp;lt;tex&amp;gt; S &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;-й символы включительно.&lt;br /&gt;
* Пусть длина строки &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;. Обозначим &amp;lt;tex&amp;gt; S^*[i..j] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; j \geqslant n &amp;lt;/tex&amp;gt; как строку &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt;, дополненную защитными символами &amp;lt;tex&amp;gt; \$ &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;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Четным суффиксом''' назовем суффикс, начинающийся в четной позиции. &amp;lt;br&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;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left( \dfrac{n}{2}\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для упрощения алгоритма вначале дополним нашу строку до четной длины (например, добавлением &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; в конец). На шаге слияния мы сможем избавиться от него.&lt;br /&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;
&lt;br /&gt;
# Отобразим исходную строку &amp;lt;tex&amp;gt; S &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; длины  &amp;lt;tex&amp;gt; \dfrac{n}{2} &amp;lt;/tex&amp;gt;  следующим образом:&lt;br /&gt;
#* Сделаем список, состоящий из пар символов вида &amp;lt;tex&amp;gt; S[i..i + 1] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; i \bmod 2 = 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его цифровой сортировкой за линейное время и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S[1..n] &amp;lt;/tex&amp;gt; в алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;, получив строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; половинной длины.&lt;br /&gt;
# Рекурсивно построим суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Построим суффиксный массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;. Очевидно, &amp;lt;tex&amp;gt; A_{S_o}[i] = 2 A_{S'}[i] + 1 &amp;lt;/tex&amp;gt;, так отношение упорядоченности любых двух строк в старом алфавите &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt; эквивалентно отношению упорядоченности в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; по его построению.&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для четных суффиксов=====&lt;br /&gt;
На этом шаге мы за линейное время получим суффиксный массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; для четных суффиксов, используя уже построенный &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что сортировка множества четных суффиксов &amp;lt;tex&amp;gt; \{ S[i..n] \mid i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке множества пар &amp;lt;tex&amp;gt; \{ (S[i], S[i+1..n]) \mid i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt;. Однако &amp;lt;tex&amp;gt; S[i+1..n] &amp;lt;/tex&amp;gt; — нечетный суффикс, и его относительную позицию мы уже узнали на шаге 1.&lt;br /&gt;
&lt;br /&gt;
Таким образом, чтобы отсортировать эти пары за линейное время, сначала сразу выпишем их в порядке возрастания второго элемента пары (то есть в порядке вхождения в массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;), а потом отсортируем устойчивой сортировкой подсчетом по первым элементам. После этого легко можно восстановить массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
     M.add(Pair(S[&amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
    &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&gt;
&lt;br /&gt;
Заметим, что массив &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; перед сортировкой подсчетом не был явно отсортирован по вторым элементам, и хранил не суффиксы, а их позиции в строке &amp;lt;tex&amp;gt; S &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;
Для суффиксного дерева третий шаг алгоритма опирается на специфические особенности суффиксных деревьев, которые не присущи суффиксным массивам&amp;lt;ref&amp;gt;M. Farach. Optimal suffix tree construction with large alphabets. http://www.cs.rutgers.edu/~farach/pubs/FarFerrMuthu00.pdf &amp;lt;/ref&amp;gt; .  &lt;br /&gt;
В случае суффиксного массива слияние становится очень сложным, но все же оно было реализовано в алгоритме Ким-Сим-Парк-Парка&amp;lt;ref&amp;gt; D. K. Kim, J. S. Sim, H. Park, and K. Park. Linear-time construction of suffix arrays. http://www.springerlink.com/content/568156021q45r320/&amp;lt;/ref&amp;gt;. Однако простой модификацией алгоритма можно значительно упростить его.&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
Покажем первые два шага агоритма для строки '''ababbbaa'''.&lt;br /&gt;
&lt;br /&gt;
Во-первых, добавив защитный символ '''$''', получив строку '''ababbbaa$''' (для этого алгоритма он не требуется, но может понадобиться в применениях суффиксного массива). Во-вторых, дополним ее до четной длины, получив '''ababbbaa$$'''.&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для нечетных суффиксов=====&lt;br /&gt;
# В новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет четыре элемента — '''ba''', '''bb''', '''a$''', '''$$'''. После сортировки они получат номера 2, 3, 1 и 0 соответственно.&lt;br /&gt;
# Переводим строку S[1..n] = '''babbbaa$$$''' в новый алфавит. Сжатой строкой &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; будет '''23210'''.&lt;br /&gt;
# После рекурсивного вызова получим, что &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [4, 3, 2, 0, 1], и &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; = [9, 7, 5, 1, 3].&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для четных суффиксов=====&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''$''', 9), ('''a''', 7), ('''b''', 5), ('''a''', 1), ('''a''', 3)].&lt;br /&gt;
# После сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt;= [('''$''', 9), ('''a''', 7), ('''a''', 1), ('''a''', 3), ('''b''', 5)].&lt;br /&gt;
# Восстановив массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;, получаем [8, 6, 0, 2, 4], что действительно является суффиксным массивом для четных суффиксов.&lt;br /&gt;
&lt;br /&gt;
=====Слияние суффиксных массивов=====&lt;br /&gt;
Если бы мы умели сливать &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; за линейное время, получили бы:&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 9 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; a\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; aa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ababbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; baa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; babbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Как и было сказано вначале, избавиться от лишних '''$''' легко, так как суффиксы, им соответствующие, будут первыми в суффиксном массиве (в данном случае достаточно выбросить &amp;quot;9&amp;quot; из суффиксного массива).&lt;br /&gt;
&lt;br /&gt;
== Алгоритм Карккайнена-Сандерса == &lt;br /&gt;
Изменим изначальный алгоритм следующим образом:&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих позициям, не кратным трем. Рекурсивно сведем это к построению суффиксного массива для строки длиной в две трети исходной.&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих кратным трем позициям, используя результат первого шага, за линейное время.&lt;br /&gt;
# Сольем эти суффиксные массивы в один за линейное время.&lt;br /&gt;
&lt;br /&gt;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left(\dfrac23 n\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого также является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt; (это видно из того, что сумма геометрической прогрессии с основанием &amp;lt;tex&amp;gt; \dfrac23 &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Аналогично первой версии алгоритма, дополним строку &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; до длины, кратной трем, защитными символами &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* '''База рекурсии'''&lt;br /&gt;
Для этого алгоритма минимальной базой рекурсии будет строка длиной 4, так как она дополняется до длины 6, после чего вновь следует рекурсивный вызов строки длиной 4, и, если бы база была меньше 4, алгоритм вошел бы в бесконечную рекурсию. На этом этапе также можно применить обычную сортировку суффиксов, так как это потребует &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
На этом шаге строится суффиксный массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; для множества суффиксов &amp;lt;tex&amp;gt; \{ S[i..n-1] \mid i \bmod 3 \ne 0 \} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Получим строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; аналогично предыдущему алгоритму:&lt;br /&gt;
#* Сделаем список, состоящий из троек &amp;lt;tex&amp;gt; S[i..i+2]&amp;lt;/tex&amp;gt; , где &amp;lt;tex&amp;gt; i \bmod 3 \ne 0 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его за линейное время цифровой сортировкой и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S[1..n]S[2..n+1] &amp;lt;/tex&amp;gt; в строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; длиной &amp;lt;tex&amp;gt; \dfrac23 n &amp;lt;/tex&amp;gt; в алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;. Тогда суффиксу &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt; в старом алфавите, где &amp;lt;tex&amp;gt; i \bmod 3 = 1 &amp;lt;/tex&amp;gt;, в новом алфавите будет соответствовать строка &amp;lt;tex&amp;gt; S'\left[\dfrac{i-1}{3}..\dfrac{n}{3} - 1\right] &amp;lt;/tex&amp;gt;, а если &amp;lt;tex&amp;gt; i\bmod 3 = 2 &amp;lt;/tex&amp;gt;, то строка &amp;lt;tex&amp;gt; S'\left[\dfrac{n}{3} + \dfrac{i-2}{3}..\dfrac{2n}{3} - 1\right] &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Вызовем алгоритм рекурсивно для строки &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt;, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Пройдем по массиву &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt; A_{S'}[i] &amp;lt; \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j = 3A_{S'}[i] + 1 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;, если же &amp;lt;tex&amp;gt; A_{S'}[i] \geqslant \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j =  3\left(A_{S'}[i] - \dfrac{n}{3}\right) + 2 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;. Псевдокод получения &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;.length - 1:&lt;br /&gt;
    '''if''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] &amp;lt; n/3:&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] + 1)&lt;br /&gt;
    '''else''':&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * (&amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] - n/3) + 2)&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
Этот шаг также аналогичен первой версии алгоритма. Сортировка множества &amp;lt;tex&amp;gt; \{ S[i..n-1] \mid i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке пар &amp;lt;tex&amp;gt; \{ (S[i], S[i+1..n-1]) \mid i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; S[i+1..n-1] &amp;lt;/tex&amp;gt; — суффиксы в позициях, равных 1 по модулю 3, относительный порядок которых уже известен. Выпишем эти пары в порядке вхождения их в &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и отсортируем по первому элементу устойчивой сортировкой подсчетом, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;. Псевдокод этого шага:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' 2n/3 - 1:&lt;br /&gt;
     '''if''' &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[i] % 3 == 1:&lt;br /&gt;
         M.add(Pair(S[&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 '''for''' i = 0 '''to''' n/3 - 1:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&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; A_{S_0} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, чтобы получить суффиксный массив &amp;lt;tex&amp;gt; A_{S} &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;
&lt;br /&gt;
Пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 1 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Это аналогично сравнению пар &amp;lt;tex&amp;gt; (S[i], S[i+1..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S[j], S[j+1..n-1]) &amp;lt;/tex&amp;gt;. Сравнить первые элементы пар мы можем за &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt;, а относительный порядок вторых элементов пар нам уже известен, так как они соответствуют позициям, равным 2 и 1 по модулю 3 соответственно.&lt;br /&gt;
&lt;br /&gt;
Аналогично, пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 2 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Тогда это аналогично сравнению троек &amp;lt;tex&amp;gt; (S[i], S[i+1], S[i+2..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S[j], S[j+1], S[j+2..n-1]) &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;A_{S}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 // Вначале предподсчитаем за O(n) обратную перестановку для суффиксного массива &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;, то есть массив rank такой, что &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[rank[i]] = i.&lt;br /&gt;
 // Тогда мы сможем за O(1) сравнивать суффиксы по их позиции.&lt;br /&gt;
 rank = inverse(&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;) &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3 '''and''' j &amp;lt; n/3:&lt;br /&gt;
     pos12 = &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i]&lt;br /&gt;
     pos0  = &amp;lt;tex&amp;gt; A_{0} &amp;lt;/tex&amp;gt;[j]&lt;br /&gt;
     '''if''' pos12 % 3 == 1:&lt;br /&gt;
        ''' if''' Pair(S[pos12], rank[pos12 + 1]) &amp;lt; Pair(S[pos0], rank[pos0 + 1]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++  &lt;br /&gt;
     '''else''':&lt;br /&gt;
         '''if''' Triple(S[pos12], S[pos12 + 1], rank[pos12 + 2]) &amp;lt; Triple(S[pos0], S[pos0 + 1], rank[pos0 + 2]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++ &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i])&lt;br /&gt;
     i++&lt;br /&gt;
 '''while''' j &amp;lt; n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{0}} &amp;lt;/tex&amp;gt;[j])&lt;br /&gt;
     j++&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;
Построим суффиксный массив для строки '''abbacab'''. После добавления защитного символа и дополнения до кратной трем длины, получим '''abbacab$$'''.&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
# Тройками, соответствующими равными 1 по модулю 3 позициям, будут: '''bba''', '''cab''', '''$$$''', соответствующими равным 2 по модулю 3 — '''bac''', '''cab$''', '''$$$'''. Новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет содержать элементы '''bba''', '''cab''', '''$$$''', '''bac''', '''ab$''', которые после сортировки получат номера 3, 4, 0, 2, 1 соответственно.&lt;br /&gt;
# Строкой '''bbacab$$$bacab$$$$''' в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; = 340210. &lt;br /&gt;
# После рекурсивного вызова получим &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [5, 2, 4, 3, 0, 1]. Пересчитав &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, получим [(5 - 3)*3 + 2, 2 * 3 + 1, (4 - 3) * 3 + 2, (3 - 3) * 3 + 2, 0 * 3 + 1, 1 * 3 + 1] = [8, 7, 5, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и выбрав в нем элементы, равные 1 по модулю 3, получим массив пар &amp;lt;tex&amp;gt;M&amp;lt;/tex&amp;gt; = [(&amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, 7), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 1), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 4)]&lt;br /&gt;
# После устойчивой сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''a''', 1), ('''a''', 4), ('''b''', 7)]&lt;br /&gt;
# Восстановив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;, получаем [0, 3, 6].&lt;br /&gt;
* '''Слияние суффиксных массивов'''&lt;br /&gt;
Рассмотрим, к примеру, третью итерацию слияния, к этой итерации массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 2, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0, на ней мы сливаем суффиксы, соответствующие позициям 5 и 0. &lt;br /&gt;
# Образуем тройки (S[5], S[6], S[7..8]) и (S[0], S[1], S[2..8]).&lt;br /&gt;
# После получения относительного порядка суффиксов, получим тройки ('''a''', '''b''', 1) и ('''a''', '''b''', 3). Первая тройка меньше второй, поэтому добавляем суффикс, соответствующий позиции 5 в массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# В конце итерации получаем &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 3, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0.&lt;br /&gt;
К концу слияния получим &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5, 0, 3, 6, 2, 1, 4]. Так как мы добавляли один символ &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; в начале алгоритма для дополнения строки до длины, кратной трем, выбросим последний суффикс из &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;, получим в итоге, что &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [7, 5, 0, 3, 6, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; acab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; b\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; cab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
Заметим, что № 9 будет выброшен, так как в начале алгоритма был добавлен один &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt; к строке&lt;br /&gt;
 &lt;br /&gt;
{|&lt;br /&gt;
| [[Файл:Kark_sanders_stage1.png|325px|thumb| Фаза 1]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage2.png|325px|thumb| Фаза 2]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage3.png|325px|thumb| Фаза 3]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Получение массива LCP ==&lt;br /&gt;
Массив LCP можно получить за линейное время [[Алгоритм_Касаи_и_др. | алгоритмом Касаи]].&lt;br /&gt;
&lt;br /&gt;
== Обобщение алгоритма ==&lt;br /&gt;
На самом деле, алгоритм можно обобщить&amp;lt;ref name=&amp;quot;generalisation&amp;quot;&amp;gt; Juha Kärkkäinen, Peter Sanders and Stefan Burkhardt. Linear work suffix array construction. http://www.cs.helsinki.fi/juha.karkkainen/publications/jacm05-revised.pdf &amp;lt;/ref&amp;gt;, взяв на первом шаге, к примеру, суффиксы, позиции которых по модулю 7 дают 3, 5 и 6. Для этого потребуются некоторое усложнение алгоритма, например, сортировка оставшихся суффиксов в нескольких группах на шаге 2 и слияние нескольких групп на шаге 3, но основная идея алгоритма остается той же. Множества, которые можно выбрать, на первом шаге определяются '''разностным покрытием''' (''difference cover'').&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Разностное покрытие''' (''difference cover'') &amp;lt;tex&amp;gt; D &amp;lt;/tex&amp;gt; по модулю &amp;lt;tex&amp;gt;m &amp;lt;/tex&amp;gt; — множество чисел от &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;m - 1 &amp;lt;/tex&amp;gt; таких, что &amp;lt;tex&amp;gt; \forall i \in [0, m-1]: \exists j, k \in D: i \equiv k - j \pmod m &amp;lt;/tex&amp;gt;.&lt;br /&gt;
}} &lt;br /&gt;
Например, &amp;lt;tex&amp;gt; \{1, 2\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt; \{3, 5, 6\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt; \{1\} &amp;lt;/tex&amp;gt; — не является разностным покрытием по модулю &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;, поэтому этот алгоритм не применим к нему. Подробнее узнать, как вычислять разностное покрытие для заданного модуля можно также здесь&amp;lt;ref name=&amp;quot;generalisation&amp;quot;/&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Источники информации==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
* [http://www.cs.helsinki.fi/juha.karkkainen/publications/icalp03.pdf Juha Kärkkäinen and Peter Sanders. Simple linear work suffix array construction]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Суффиксный массив]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=48194</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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=48194"/>
				<updated>2015-06-09T05:57:41Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Суффиксный массив для четных суффиксов */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Алгоритм Карккайнена-Сандерса''' (Kärkkäinen, Sanders) — алгоритм построения [[суффиксный массив | суффиксного массива]] за линейное время.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Используемые обозначения ==&lt;br /&gt;
* В данном конспекте используется 0-индексация.&lt;br /&gt;
* &amp;lt;tex&amp;gt;S[i..j] &amp;lt;/tex&amp;gt; — подстрока строки &amp;lt;tex&amp;gt; S &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;-й символы включительно.&lt;br /&gt;
* Пусть длина строки &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;. Обозначим &amp;lt;tex&amp;gt; S^*[i..j] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; j \geqslant n &amp;lt;/tex&amp;gt; как строку &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt;, дополненную защитными символами &amp;lt;tex&amp;gt; \$ &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;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Четным суффиксом''' назовем суффикс, начинающийся в четной позиции. &amp;lt;br&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;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left( \dfrac{n}{2}\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для упрощения алгоритма вначале дополним нашу строку до четной длины (например, добавлением &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; в конец). На шаге слияния мы сможем избавиться от него.&lt;br /&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;
&lt;br /&gt;
# Отобразим исходную строку &amp;lt;tex&amp;gt; S &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; длины  &amp;lt;tex&amp;gt; \dfrac{n}{2} &amp;lt;/tex&amp;gt;  следующим образом:&lt;br /&gt;
#* Сделаем список, состоящий из пар символов вида &amp;lt;tex&amp;gt; S[i..i + 1] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; i \bmod 2 = 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его цифровой сортировкой за линейное время и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S[1..n] &amp;lt;/tex&amp;gt; в алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;, получив строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; половинной длины.&lt;br /&gt;
# Рекурсивно построим суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Построим суффиксный массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;. Очевидно, &amp;lt;tex&amp;gt; A_{S_o}[i] = 2 A_{S'}[i] + 1 &amp;lt;/tex&amp;gt;, так отношение упорядоченности любых двух строк в старом алфавите &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt; эквивалентно отношению упорядоченности в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; по его построению.&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для четных суффиксов=====&lt;br /&gt;
На этом шаге мы за линейное время получим суффиксный массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; для четных суффиксов, используя уже построенный &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что сортировка множества четных суффиксов &amp;lt;tex&amp;gt; \{ S[i..n] \mid i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке множества пар &amp;lt;tex&amp;gt; \{ (S[i], S[i+1..n]) \mid i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt;. Однако &amp;lt;tex&amp;gt; S[i+1..n] &amp;lt;/tex&amp;gt; — нечетный суффикс, и его относительную позицию мы уже узнали на шаге 1.&lt;br /&gt;
&lt;br /&gt;
Таким образом, чтобы отсортировать эти пары за линейное время, сначала сразу выпишем их в порядке возрастания второго элемента пары (то есть в порядке вхождения в массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;), а потом отсортируем устойчивой сортировкой подсчетом по первым элементам. После этого легко можно восстановить массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
     M.add(Pair(S[&amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
    &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&gt;
&lt;br /&gt;
Заметим, что массив &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; перед сортировкой подсчетом не был явно отсортирован по вторым элементам, и хранил не суффиксы, а их позиции в строке &amp;lt;tex&amp;gt; S &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;
Для суффиксного дерева третий шаг алгоритма опирается на специфические особенности суффиксных деревьев, которые не присущи суффиксным массивам&amp;lt;ref&amp;gt;M. Farach. Optimal suffix tree construction with large alphabets. http://www.cs.rutgers.edu/~farach/pubs/FarFerrMuthu00.pdf &amp;lt;/ref&amp;gt; .  &lt;br /&gt;
В случае суффиксного массива слияние становится очень сложным, но все же оно было реализовано в алгоритме Ким-Сим-Парк-Парка&amp;lt;ref&amp;gt; D. K. Kim, J. S. Sim, H. Park, and K. Park. Linear-time construction of suffix arrays. http://www.springerlink.com/content/568156021q45r320/&amp;lt;/ref&amp;gt;. Однако простой модификацией алгоритма можно значительно упростить его.&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
Покажем первые два шага агоритма для строки '''ababbbaa'''.&lt;br /&gt;
&lt;br /&gt;
Во-первых, добавив защитный символ '''$''', получив строку '''ababbbaa$''' (для этого алгоритма он не требуется, но может понадобиться в применениях суффиксного массива). Во-вторых, дополним ее до четной длины, получив '''ababbbaa$$'''.&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для нечетных суффиксов=====&lt;br /&gt;
# В новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет четыре элемента — '''ba''', '''bb''', '''a$''', '''$$'''. После сортировки они получат номера 2, 3, 1 и 0 соответственно.&lt;br /&gt;
# Переводим строку S[1..n] = '''babbbaa$$$''' в новый алфавит. Сжатой строкой &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; будет '''23210'''.&lt;br /&gt;
# После рекурсивного вызова получим, что &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [4, 3, 2, 0, 1], и &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; = [9, 7, 5, 1, 3].&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для четных суффиксов=====&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''$''', 9), ('''a''', 7), ('''b''', 5), ('''a''', 1), ('''a''', 3)].&lt;br /&gt;
# После сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt;= [('''$''', 9), ('''a''', 7), ('''a''', 1), ('''a''', 3), ('''b''', 5)].&lt;br /&gt;
# Восстановив массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;, получаем [8, 6, 0, 2, 4], что действительно является суффиксным массивом для четных суффиксов.&lt;br /&gt;
&lt;br /&gt;
=====Слияние суффиксных массивов=====&lt;br /&gt;
Если бы мы умели сливать &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; за линейное время, получили бы:&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 9 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; a\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; aa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ababbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; baa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; babbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Как и было сказано вначале, избавиться от лишних '''$''' легко, так как суффиксы, им соответствующие, будут первыми в суффиксном массиве (в данном случае достаточно выбросить &amp;quot;9&amp;quot; из суффиксного массива).&lt;br /&gt;
&lt;br /&gt;
== Алгоритм Карккайнена-Сандерса == &lt;br /&gt;
Изменим изначальный алгоритм следующим образом:&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих позициям, не кратным трем. Рекурсивно сведем это к построению суффиксного массива для строки длиной в две трети исходной.&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих кратным трем позициям, используя результат первого шага, за линейное время.&lt;br /&gt;
# Сольем эти суффиксные массивы в один за линейное время.&lt;br /&gt;
&lt;br /&gt;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left(\dfrac23 n\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого также является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt; (это видно из того, что сумма геометрической прогрессии с основанием &amp;lt;tex&amp;gt; \dfrac23 &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Аналогично первой версии алгоритма, дополним строку &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; до длины, кратной трем, защитными символами &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* '''База рекурсии'''&lt;br /&gt;
Для этого алгоритма минимальной базой рекурсии будет строка длиной 4, так как она дополняется до длины 6, после чего вновь следует рекурсивный вызов строки длиной 4, и, если бы база была меньше 4, алгоритм вошел бы в бесконечную рекурсию. На этом этапе также можно применить обычную сортировку суффиксов, так как это потребует &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
На этом шаге строится суффиксный массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; для множества суффиксов &amp;lt;tex&amp;gt; \{ S[i..n-1] | i \bmod 3 \ne 0 \} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Получим строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; аналогично предыдущему алгоритму:&lt;br /&gt;
#* Сделаем список, состоящий из троек &amp;lt;tex&amp;gt; S[i..i+2]&amp;lt;/tex&amp;gt; , где &amp;lt;tex&amp;gt; i \bmod 3 \ne 0 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его за линейное время цифровой сортировкой и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S[1..n]S[2..n+1] &amp;lt;/tex&amp;gt; в строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; длиной &amp;lt;tex&amp;gt; \dfrac23 n &amp;lt;/tex&amp;gt; в алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;. Тогда суффиксу &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt; в старом алфавите, где &amp;lt;tex&amp;gt; i \bmod 3 = 1 &amp;lt;/tex&amp;gt;, в новом алфавите будет соответствовать строка &amp;lt;tex&amp;gt; S'\left[\dfrac{i-1}{3}..\dfrac{n}{3} - 1\right] &amp;lt;/tex&amp;gt;, а если &amp;lt;tex&amp;gt; i\bmod 3 = 2 &amp;lt;/tex&amp;gt;, то строка &amp;lt;tex&amp;gt; S'\left[\dfrac{n}{3} + \dfrac{i-2}{3}..\dfrac{2n}{3} - 1\right] &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Вызовем алгоритм рекурсивно для строки &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt;, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Пройдем по массиву &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt; A_{S'}[i] &amp;lt; \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j = 3A_{S'}[i] + 1 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;, если же &amp;lt;tex&amp;gt; A_{S'}[i] \geqslant \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j =  3\left(A_{S'}[i] - \dfrac{n}{3}\right) + 2 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;. Псевдокод получения &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;.length - 1:&lt;br /&gt;
    '''if''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] &amp;lt; n/3:&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] + 1)&lt;br /&gt;
    '''else''':&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * (&amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] - n/3) + 2)&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
Этот шаг также аналогичен первой версии алгоритма. Сортировка множества &amp;lt;tex&amp;gt; \{ S[i..n-1] | i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке пар &amp;lt;tex&amp;gt; \{ (S[i], S[i+1..n-1]) | i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; S[i+1..n-1] &amp;lt;/tex&amp;gt; — суффиксы в позициях, равных 1 по модулю 3, относительный порядок которых уже известен. Выпишем эти пары в порядке вхождения их в &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и отсортируем по первому элементу устойчивой сортировкой подсчетом, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;. Псевдокод этого шага:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' 2n/3 - 1:&lt;br /&gt;
     '''if''' &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[i] % 3 == 1:&lt;br /&gt;
         M.add(Pair(S[&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 '''for''' i = 0 '''to''' n/3 - 1:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&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; A_{S_0} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, чтобы получить суффиксный массив &amp;lt;tex&amp;gt; A_{S} &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;
&lt;br /&gt;
Пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 1 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Это аналогично сравнению пар &amp;lt;tex&amp;gt; (S[i], S[i+1..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S[j], S[j+1..n-1]) &amp;lt;/tex&amp;gt;. Сравнить первые элементы пар мы можем за &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt;, а относительный порядок вторых элементов пар нам уже известен, так как они соответствуют позициям, равным 2 и 1 по модулю 3 соответственно.&lt;br /&gt;
&lt;br /&gt;
Аналогично, пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 2 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Тогда это аналогично сравнению троек &amp;lt;tex&amp;gt; (S[i], S[i+1], S[i+2..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S[j], S[j+1], S[j+2..n-1]) &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;A_{S}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 // Вначале предподсчитаем за O(n) обратную перестановку для суффиксного массива &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;, то есть массив rank такой, что &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[rank[i]] = i.&lt;br /&gt;
 // Тогда мы сможем за O(1) сравнивать суффиксы по их позиции.&lt;br /&gt;
 rank = inverse(&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;) &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3 '''and''' j &amp;lt; n/3:&lt;br /&gt;
     pos12 = &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i]&lt;br /&gt;
     pos0  = &amp;lt;tex&amp;gt; A_{0} &amp;lt;/tex&amp;gt;[j]&lt;br /&gt;
     '''if''' pos12 % 3 == 1:&lt;br /&gt;
        ''' if''' Pair(S[pos12], rank[pos12 + 1]) &amp;lt; Pair(S[pos0], rank[pos0 + 1]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++  &lt;br /&gt;
     '''else''':&lt;br /&gt;
         '''if''' Triple(S[pos12], S[pos12 + 1], rank[pos12 + 2]) &amp;lt; Triple(S[pos0], S[pos0 + 1], rank[pos0 + 2]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++ &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i])&lt;br /&gt;
     i++&lt;br /&gt;
 '''while''' j &amp;lt; n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{0}} &amp;lt;/tex&amp;gt;[j])&lt;br /&gt;
     j++&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;
Построим суффиксный массив для строки '''abbacab'''. После добавления защитного символа и дополнения до кратной трем длины, получим '''abbacab$$'''.&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
# Тройками, соответствующими равными 1 по модулю 3 позициям, будут: '''bba''', '''cab''', '''$$$''', соответствующими равным 2 по модулю 3 — '''bac''', '''cab$''', '''$$$'''. Новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет содержать элементы '''bba''', '''cab''', '''$$$''', '''bac''', '''ab$''', которые после сортировки получат номера 3, 4, 0, 2, 1 соответственно.&lt;br /&gt;
# Строкой '''bbacab$$$bacab$$$$''' в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; = 340210. &lt;br /&gt;
# После рекурсивного вызова получим &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [5, 2, 4, 3, 0, 1]. Пересчитав &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, получим [(5 - 3)*3 + 2, 2 * 3 + 1, (4 - 3) * 3 + 2, (3 - 3) * 3 + 2, 0 * 3 + 1, 1 * 3 + 1] = [8, 7, 5, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и выбрав в нем элементы, равные 1 по модулю 3, получим массив пар &amp;lt;tex&amp;gt;M&amp;lt;/tex&amp;gt; = [(&amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, 7), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 1), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 4)]&lt;br /&gt;
# После устойчивой сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''a''', 1), ('''a''', 4), ('''b''', 7)]&lt;br /&gt;
# Восстановив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;, получаем [0, 3, 6].&lt;br /&gt;
* '''Слияние суффиксных массивов'''&lt;br /&gt;
Рассмотрим, к примеру, третью итерацию слияния, к этой итерации массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 2, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0, на ней мы сливаем суффиксы, соответствующие позициям 5 и 0. &lt;br /&gt;
# Образуем тройки (S[5], S[6], S[7..8]) и (S[0], S[1], S[2..8]).&lt;br /&gt;
# После получения относительного порядка суффиксов, получим тройки ('''a''', '''b''', 1) и ('''a''', '''b''', 3). Первая тройка меньше второй, поэтому добавляем суффикс, соответствующий позиции 5 в массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# В конце итерации получаем &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 3, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0.&lt;br /&gt;
К концу слияния получим &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5, 0, 3, 6, 2, 1, 4]. Так как мы добавляли один символ &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; в начале алгоритма для дополнения строки до длины, кратной трем, выбросим последний суффикс из &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;, получим в итоге, что &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [7, 5, 0, 3, 6, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; acab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; b\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; cab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
Заметим, что №9 будет выброшен, так как в начале алгоритма был добавлен один &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt; к строке&lt;br /&gt;
 &lt;br /&gt;
{|&lt;br /&gt;
| [[Файл:Kark_sanders_stage1.png|325px|thumb| Фаза 1]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage2.png|325px|thumb| Фаза 2]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage3.png|325px|thumb| Фаза 3]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Получение массива LCP ==&lt;br /&gt;
Массив LCP можно получить за линейное время [[Алгоритм_Касаи_и_др. | алгоритмом Касаи]].&lt;br /&gt;
&lt;br /&gt;
== Обобщение алгоритма ==&lt;br /&gt;
На самом деле, алгоритм можно обобщить&amp;lt;ref name=&amp;quot;generalisation&amp;quot;&amp;gt; Juha Kärkkäinen, Peter Sanders and Stefan Burkhardt. Linear work suffix array construction. http://www.cs.helsinki.fi/juha.karkkainen/publications/jacm05-revised.pdf &amp;lt;/ref&amp;gt;, взяв на первом шаге, к примеру, суффиксы, позиции которых по модулю 7 дают 3, 5 и 6. Для этого потребуются некоторое усложнение алгоритма, например, сортировка оставшихся суффиксов в нескольких группах на шаге 2 и слияние нескольких групп на шаге 3, но основная идея алгоритма остается той же. Множества, которые можно выбрать, на первом шаге определяются '''разностным покрытием''' (''difference cover'').&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Разностное покрытие''' (''difference cover'') &amp;lt;tex&amp;gt; D &amp;lt;/tex&amp;gt; по модулю &amp;lt;tex&amp;gt;m &amp;lt;/tex&amp;gt; — множество чисел от &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;m - 1 &amp;lt;/tex&amp;gt; таких, что &amp;lt;tex&amp;gt; \forall i \in [0, m-1]: \exists j, k \in D: i \equiv k - j \pmod m &amp;lt;/tex&amp;gt;.&lt;br /&gt;
}} &lt;br /&gt;
Например, &amp;lt;tex&amp;gt; \{1, 2\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt; \{3, 5, 6\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt; \{1\} &amp;lt;/tex&amp;gt; — не является разностным покрытием по модулю &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;, поэтому этот алгоритм не применим к нему. Подробнее узнать, как вычислять разностное покрытие для заданного модуля можно также здесь&amp;lt;ref name=&amp;quot;generalisation&amp;quot;/&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Источники информации==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
* [http://www.cs.helsinki.fi/juha.karkkainen/publications/icalp03.pdf Juha Kärkkäinen and Peter Sanders. Simple linear work suffix array construction]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Суффиксный массив]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=48193</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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=48193"/>
				<updated>2015-06-09T05:55:22Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Алгоритм «разделяй и властвуй» */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Алгоритм Карккайнена-Сандерса''' (Kärkkäinen, Sanders) — алгоритм построения [[суффиксный массив | суффиксного массива]] за линейное время.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Используемые обозначения ==&lt;br /&gt;
* В данном конспекте используется 0-индексация.&lt;br /&gt;
* &amp;lt;tex&amp;gt;S[i..j] &amp;lt;/tex&amp;gt; — подстрока строки &amp;lt;tex&amp;gt; S &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;-й символы включительно.&lt;br /&gt;
* Пусть длина строки &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;. Обозначим &amp;lt;tex&amp;gt; S^*[i..j] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; j \geqslant n &amp;lt;/tex&amp;gt; как строку &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt;, дополненную защитными символами &amp;lt;tex&amp;gt; \$ &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;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Четным суффиксом''' назовем суффикс, начинающийся в четной позиции. &amp;lt;br&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;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left( \dfrac{n}{2}\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для упрощения алгоритма вначале дополним нашу строку до четной длины (например, добавлением &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; в конец). На шаге слияния мы сможем избавиться от него.&lt;br /&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;
&lt;br /&gt;
# Отобразим исходную строку &amp;lt;tex&amp;gt; S &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; длины  &amp;lt;tex&amp;gt; \dfrac{n}{2} &amp;lt;/tex&amp;gt;  следующим образом:&lt;br /&gt;
#* Сделаем список, состоящий из пар символов вида &amp;lt;tex&amp;gt; S[i..i + 1] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; i \bmod 2 = 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его цифровой сортировкой за линейное время и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S[1..n] &amp;lt;/tex&amp;gt; в алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;, получив строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; половинной длины.&lt;br /&gt;
# Рекурсивно построим суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Построим суффиксный массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;. Очевидно, &amp;lt;tex&amp;gt; A_{S_o}[i] = 2 A_{S'}[i] + 1 &amp;lt;/tex&amp;gt;, так отношение упорядоченности любых двух строк в старом алфавите &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt; эквивалентно отношению упорядоченности в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; по его построению.&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для четных суффиксов=====&lt;br /&gt;
На этом шаге мы за линейное время получим суффиксный массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; для четных суффиксов, используя уже построенный &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что сортировка множества четных суффиксов &amp;lt;tex&amp;gt; \{ S[i..n] | i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке множества пар &amp;lt;tex&amp;gt; \{ (S[i], S[i+1..n]) | i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt;. Однако &amp;lt;tex&amp;gt; S[i+1..n] &amp;lt;/tex&amp;gt; — нечетный суффикс, и его относительную позицию мы уже узнали на шаге 1.&lt;br /&gt;
&lt;br /&gt;
Таким образом, чтобы отсортировать эти пары за линейное время, сначала сразу выпишем их в порядке возрастания второго элемента пары (то есть в порядке вхождения в массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;), а потом отсортируем устойчивой сортировкой подсчетом по первым элементам. После этого легко можно восстановить массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
     M.add(Pair(S[&amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
    &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&gt;
&lt;br /&gt;
Заметим, что массив &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; перед сортировкой подсчетом не был явно отсортирован по вторым элементам, и хранил не суффиксы, а их позиции в строке &amp;lt;tex&amp;gt; S &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;
Для суффиксного дерева третий шаг алгоритма опирается на специфические особенности суффиксных деревьев, которые не присущи суффиксным массивам&amp;lt;ref&amp;gt;M. Farach. Optimal suffix tree construction with large alphabets. http://www.cs.rutgers.edu/~farach/pubs/FarFerrMuthu00.pdf &amp;lt;/ref&amp;gt; .  &lt;br /&gt;
В случае суффиксного массива слияние становится очень сложным, но все же оно было реализовано в алгоритме Ким-Сим-Парк-Парка&amp;lt;ref&amp;gt; D. K. Kim, J. S. Sim, H. Park, and K. Park. Linear-time construction of suffix arrays. http://www.springerlink.com/content/568156021q45r320/&amp;lt;/ref&amp;gt;. Однако простой модификацией алгоритма можно значительно упростить его.&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
Покажем первые два шага агоритма для строки '''ababbbaa'''.&lt;br /&gt;
&lt;br /&gt;
Во-первых, добавив защитный символ '''$''', получив строку '''ababbbaa$''' (для этого алгоритма он не требуется, но может понадобиться в применениях суффиксного массива). Во-вторых, дополним ее до четной длины, получив '''ababbbaa$$'''.&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для нечетных суффиксов=====&lt;br /&gt;
# В новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет четыре элемента — '''ba''', '''bb''', '''a$''', '''$$'''. После сортировки они получат номера 2, 3, 1 и 0 соответственно.&lt;br /&gt;
# Переводим строку S[1..n] = '''babbbaa$$$''' в новый алфавит. Сжатой строкой &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; будет '''23210'''.&lt;br /&gt;
# После рекурсивного вызова получим, что &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [4, 3, 2, 0, 1], и &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; = [9, 7, 5, 1, 3].&lt;br /&gt;
&lt;br /&gt;
=====Суффиксный массив для четных суффиксов=====&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''$''', 9), ('''a''', 7), ('''b''', 5), ('''a''', 1), ('''a''', 3)].&lt;br /&gt;
# После сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt;= [('''$''', 9), ('''a''', 7), ('''a''', 1), ('''a''', 3), ('''b''', 5)].&lt;br /&gt;
# Восстановив массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;, получаем [8, 6, 0, 2, 4], что действительно является суффиксным массивом для четных суффиксов.&lt;br /&gt;
&lt;br /&gt;
=====Слияние суффиксных массивов=====&lt;br /&gt;
Если бы мы умели сливать &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; за линейное время, получили бы:&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 9 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; a\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; aa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ababbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; baa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; babbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Как и было сказано вначале, избавиться от лишних '''$''' легко, так как суффиксы, им соответствующие, будут первыми в суффиксном массиве (в данном случае достаточно выбросить &amp;quot;9&amp;quot; из суффиксного массива).&lt;br /&gt;
&lt;br /&gt;
== Алгоритм Карккайнена-Сандерса == &lt;br /&gt;
Изменим изначальный алгоритм следующим образом:&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих позициям, не кратным трем. Рекурсивно сведем это к построению суффиксного массива для строки длиной в две трети исходной.&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих кратным трем позициям, используя результат первого шага, за линейное время.&lt;br /&gt;
# Сольем эти суффиксные массивы в один за линейное время.&lt;br /&gt;
&lt;br /&gt;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left(\dfrac23 n\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого также является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt; (это видно из того, что сумма геометрической прогрессии с основанием &amp;lt;tex&amp;gt; \dfrac23 &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Аналогично первой версии алгоритма, дополним строку &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; до длины, кратной трем, защитными символами &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* '''База рекурсии'''&lt;br /&gt;
Для этого алгоритма минимальной базой рекурсии будет строка длиной 4, так как она дополняется до длины 6, после чего вновь следует рекурсивный вызов строки длиной 4, и, если бы база была меньше 4, алгоритм вошел бы в бесконечную рекурсию. На этом этапе также можно применить обычную сортировку суффиксов, так как это потребует &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
На этом шаге строится суффиксный массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; для множества суффиксов &amp;lt;tex&amp;gt; \{ S[i..n-1] | i \bmod 3 \ne 0 \} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Получим строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; аналогично предыдущему алгоритму:&lt;br /&gt;
#* Сделаем список, состоящий из троек &amp;lt;tex&amp;gt; S[i..i+2]&amp;lt;/tex&amp;gt; , где &amp;lt;tex&amp;gt; i \bmod 3 \ne 0 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его за линейное время цифровой сортировкой и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S[1..n]S[2..n+1] &amp;lt;/tex&amp;gt; в строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; длиной &amp;lt;tex&amp;gt; \dfrac23 n &amp;lt;/tex&amp;gt; в алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;. Тогда суффиксу &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt; в старом алфавите, где &amp;lt;tex&amp;gt; i \bmod 3 = 1 &amp;lt;/tex&amp;gt;, в новом алфавите будет соответствовать строка &amp;lt;tex&amp;gt; S'\left[\dfrac{i-1}{3}..\dfrac{n}{3} - 1\right] &amp;lt;/tex&amp;gt;, а если &amp;lt;tex&amp;gt; i\bmod 3 = 2 &amp;lt;/tex&amp;gt;, то строка &amp;lt;tex&amp;gt; S'\left[\dfrac{n}{3} + \dfrac{i-2}{3}..\dfrac{2n}{3} - 1\right] &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Вызовем алгоритм рекурсивно для строки &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt;, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Пройдем по массиву &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt; A_{S'}[i] &amp;lt; \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j = 3A_{S'}[i] + 1 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;, если же &amp;lt;tex&amp;gt; A_{S'}[i] \geqslant \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j =  3\left(A_{S'}[i] - \dfrac{n}{3}\right) + 2 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;. Псевдокод получения &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;.length - 1:&lt;br /&gt;
    '''if''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] &amp;lt; n/3:&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] + 1)&lt;br /&gt;
    '''else''':&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * (&amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] - n/3) + 2)&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
Этот шаг также аналогичен первой версии алгоритма. Сортировка множества &amp;lt;tex&amp;gt; \{ S[i..n-1] | i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке пар &amp;lt;tex&amp;gt; \{ (S[i], S[i+1..n-1]) | i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; S[i+1..n-1] &amp;lt;/tex&amp;gt; — суффиксы в позициях, равных 1 по модулю 3, относительный порядок которых уже известен. Выпишем эти пары в порядке вхождения их в &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и отсортируем по первому элементу устойчивой сортировкой подсчетом, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;. Псевдокод этого шага:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' 2n/3 - 1:&lt;br /&gt;
     '''if''' &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[i] % 3 == 1:&lt;br /&gt;
         M.add(Pair(S[&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 '''for''' i = 0 '''to''' n/3 - 1:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&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; A_{S_0} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, чтобы получить суффиксный массив &amp;lt;tex&amp;gt; A_{S} &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;
&lt;br /&gt;
Пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 1 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Это аналогично сравнению пар &amp;lt;tex&amp;gt; (S[i], S[i+1..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S[j], S[j+1..n-1]) &amp;lt;/tex&amp;gt;. Сравнить первые элементы пар мы можем за &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt;, а относительный порядок вторых элементов пар нам уже известен, так как они соответствуют позициям, равным 2 и 1 по модулю 3 соответственно.&lt;br /&gt;
&lt;br /&gt;
Аналогично, пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 2 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Тогда это аналогично сравнению троек &amp;lt;tex&amp;gt; (S[i], S[i+1], S[i+2..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S[j], S[j+1], S[j+2..n-1]) &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;A_{S}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 // Вначале предподсчитаем за O(n) обратную перестановку для суффиксного массива &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;, то есть массив rank такой, что &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[rank[i]] = i.&lt;br /&gt;
 // Тогда мы сможем за O(1) сравнивать суффиксы по их позиции.&lt;br /&gt;
 rank = inverse(&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;) &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3 '''and''' j &amp;lt; n/3:&lt;br /&gt;
     pos12 = &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i]&lt;br /&gt;
     pos0  = &amp;lt;tex&amp;gt; A_{0} &amp;lt;/tex&amp;gt;[j]&lt;br /&gt;
     '''if''' pos12 % 3 == 1:&lt;br /&gt;
        ''' if''' Pair(S[pos12], rank[pos12 + 1]) &amp;lt; Pair(S[pos0], rank[pos0 + 1]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++  &lt;br /&gt;
     '''else''':&lt;br /&gt;
         '''if''' Triple(S[pos12], S[pos12 + 1], rank[pos12 + 2]) &amp;lt; Triple(S[pos0], S[pos0 + 1], rank[pos0 + 2]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++ &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i])&lt;br /&gt;
     i++&lt;br /&gt;
 '''while''' j &amp;lt; n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{0}} &amp;lt;/tex&amp;gt;[j])&lt;br /&gt;
     j++&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;
Построим суффиксный массив для строки '''abbacab'''. После добавления защитного символа и дополнения до кратной трем длины, получим '''abbacab$$'''.&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
# Тройками, соответствующими равными 1 по модулю 3 позициям, будут: '''bba''', '''cab''', '''$$$''', соответствующими равным 2 по модулю 3 — '''bac''', '''cab$''', '''$$$'''. Новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет содержать элементы '''bba''', '''cab''', '''$$$''', '''bac''', '''ab$''', которые после сортировки получат номера 3, 4, 0, 2, 1 соответственно.&lt;br /&gt;
# Строкой '''bbacab$$$bacab$$$$''' в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; = 340210. &lt;br /&gt;
# После рекурсивного вызова получим &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [5, 2, 4, 3, 0, 1]. Пересчитав &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, получим [(5 - 3)*3 + 2, 2 * 3 + 1, (4 - 3) * 3 + 2, (3 - 3) * 3 + 2, 0 * 3 + 1, 1 * 3 + 1] = [8, 7, 5, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и выбрав в нем элементы, равные 1 по модулю 3, получим массив пар &amp;lt;tex&amp;gt;M&amp;lt;/tex&amp;gt; = [(&amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, 7), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 1), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 4)]&lt;br /&gt;
# После устойчивой сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''a''', 1), ('''a''', 4), ('''b''', 7)]&lt;br /&gt;
# Восстановив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;, получаем [0, 3, 6].&lt;br /&gt;
* '''Слияние суффиксных массивов'''&lt;br /&gt;
Рассмотрим, к примеру, третью итерацию слияния, к этой итерации массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 2, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0, на ней мы сливаем суффиксы, соответствующие позициям 5 и 0. &lt;br /&gt;
# Образуем тройки (S[5], S[6], S[7..8]) и (S[0], S[1], S[2..8]).&lt;br /&gt;
# После получения относительного порядка суффиксов, получим тройки ('''a''', '''b''', 1) и ('''a''', '''b''', 3). Первая тройка меньше второй, поэтому добавляем суффикс, соответствующий позиции 5 в массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# В конце итерации получаем &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 3, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0.&lt;br /&gt;
К концу слияния получим &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5, 0, 3, 6, 2, 1, 4]. Так как мы добавляли один символ &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; в начале алгоритма для дополнения строки до длины, кратной трем, выбросим последний суффикс из &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;, получим в итоге, что &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [7, 5, 0, 3, 6, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; acab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; b\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; cab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
Заметим, что №9 будет выброшен, так как в начале алгоритма был добавлен один &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt; к строке&lt;br /&gt;
 &lt;br /&gt;
{|&lt;br /&gt;
| [[Файл:Kark_sanders_stage1.png|325px|thumb| Фаза 1]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage2.png|325px|thumb| Фаза 2]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage3.png|325px|thumb| Фаза 3]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Получение массива LCP ==&lt;br /&gt;
Массив LCP можно получить за линейное время [[Алгоритм_Касаи_и_др. | алгоритмом Касаи]].&lt;br /&gt;
&lt;br /&gt;
== Обобщение алгоритма ==&lt;br /&gt;
На самом деле, алгоритм можно обобщить&amp;lt;ref name=&amp;quot;generalisation&amp;quot;&amp;gt; Juha Kärkkäinen, Peter Sanders and Stefan Burkhardt. Linear work suffix array construction. http://www.cs.helsinki.fi/juha.karkkainen/publications/jacm05-revised.pdf &amp;lt;/ref&amp;gt;, взяв на первом шаге, к примеру, суффиксы, позиции которых по модулю 7 дают 3, 5 и 6. Для этого потребуются некоторое усложнение алгоритма, например, сортировка оставшихся суффиксов в нескольких группах на шаге 2 и слияние нескольких групп на шаге 3, но основная идея алгоритма остается той же. Множества, которые можно выбрать, на первом шаге определяются '''разностным покрытием''' (''difference cover'').&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Разностное покрытие''' (''difference cover'') &amp;lt;tex&amp;gt; D &amp;lt;/tex&amp;gt; по модулю &amp;lt;tex&amp;gt;m &amp;lt;/tex&amp;gt; — множество чисел от &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;m - 1 &amp;lt;/tex&amp;gt; таких, что &amp;lt;tex&amp;gt; \forall i \in [0, m-1]: \exists j, k \in D: i \equiv k - j \pmod m &amp;lt;/tex&amp;gt;.&lt;br /&gt;
}} &lt;br /&gt;
Например, &amp;lt;tex&amp;gt; \{1, 2\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt; \{3, 5, 6\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt; \{1\} &amp;lt;/tex&amp;gt; — не является разностным покрытием по модулю &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;, поэтому этот алгоритм не применим к нему. Подробнее узнать, как вычислять разностное покрытие для заданного модуля можно также здесь&amp;lt;ref name=&amp;quot;generalisation&amp;quot;/&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Источники информации==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
* [http://www.cs.helsinki.fi/juha.karkkainen/publications/icalp03.pdf Juha Kärkkäinen and Peter Sanders. Simple linear work suffix array construction]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Суффиксный массив]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=48192</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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=48192"/>
				<updated>2015-06-09T05:49:53Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Алгоритм «разделяй и властвуй» */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Алгоритм Карккайнена-Сандерса''' (Kärkkäinen, Sanders) — алгоритм построения [[суффиксный массив | суффиксного массива]] за линейное время.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Используемые обозначения ==&lt;br /&gt;
* В данном конспекте используется 0-индексация.&lt;br /&gt;
* &amp;lt;tex&amp;gt;S[i..j] &amp;lt;/tex&amp;gt; — подстрока строки &amp;lt;tex&amp;gt; S &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;-й символы включительно.&lt;br /&gt;
* Пусть длина строки &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;. Обозначим &amp;lt;tex&amp;gt; S^*[i..j] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; j \geqslant n &amp;lt;/tex&amp;gt; как строку &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt;, дополненную защитными символами &amp;lt;tex&amp;gt; \$ &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;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Четным суффиксом''' назовем суффикс, начинающийся в четной позиции. &amp;lt;br&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;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left( \dfrac{n}{2}\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для упрощения алгоритма вначале дополним нашу строку до четной длины (например, добавлением &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; в конец). На шаге слияния мы сможем избавиться от него.&lt;br /&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;
&lt;br /&gt;
# Отобразим исходную строку &amp;lt;tex&amp;gt; S &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; длины  &amp;lt;tex&amp;gt; \dfrac{n}{2} &amp;lt;/tex&amp;gt;  следующим образом:&lt;br /&gt;
#* Сделаем список, состоящий из пар символов вида &amp;lt;tex&amp;gt; S[i..i + 1] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; i \bmod 2 = 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его цифровой сортировкой за линейное время и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S[1..n] &amp;lt;/tex&amp;gt; в алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;, получив строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; половинной длины.&lt;br /&gt;
# Рекурсивно построим суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Построим суффиксный массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;. Очевидно, &amp;lt;tex&amp;gt; A_{S_o}[i] = 2 A_{S'}[i] + 1 &amp;lt;/tex&amp;gt;, так отношение упорядоченности любых двух строк в старом алфавите &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt; эквивалентно отношению упорядоченности в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; по его построению.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для четных суффиксов'''&lt;br /&gt;
На этом шаге мы за линейное время получим суффиксный массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; для четных суффиксов, используя уже построенный &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что сортировка множества четных суффиксов &amp;lt;tex&amp;gt; \{ S[i..n] | i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке множества пар &amp;lt;tex&amp;gt; \{ (S[i], S[i+1..n]) | i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt;. Однако &amp;lt;tex&amp;gt; S[i+1..n] &amp;lt;/tex&amp;gt; — нечетный суффикс, и его относительную позицию мы уже узнали на шаге 1.&lt;br /&gt;
&lt;br /&gt;
Таким образом, чтобы отсортировать эти пары за линейное время, сначала сразу выпишем их в порядке возрастания второго элемента пары (то есть в порядке вхождения в массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;), а потом отсортируем устойчивой сортировкой подсчетом по первым элементам. После этого легко можно восстановить массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
     M.add(Pair(S[&amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
    &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&gt;
&lt;br /&gt;
Заметим, что массив &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; перед сортировкой подсчетом не был явно отсортирован по вторым элементам, и хранил не суффиксы, а их позиции в строке &amp;lt;tex&amp;gt; S &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;
Для суффиксного дерева третий шаг алгоритма опирается на специфические особенности суффиксных деревьев, которые не присущи суффиксным массивам&amp;lt;ref&amp;gt;M. Farach. Optimal suffix tree construction with large alphabets. http://www.cs.rutgers.edu/~farach/pubs/FarFerrMuthu00.pdf &amp;lt;/ref&amp;gt; .  &lt;br /&gt;
В случае суффиксного массива слияние становится очень сложным, но все же оно было реализовано в алгоритме Ким-Сим-Парк-Парка&amp;lt;ref&amp;gt; D. K. Kim, J. S. Sim, H. Park, and K. Park. Linear-time construction of suffix arrays. http://www.springerlink.com/content/568156021q45r320/&amp;lt;/ref&amp;gt;. Однако простой модификацией алгоритма можно значительно упростить его.&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
Покажем первые два шага агоритма для строки '''ababbbaa'''.&lt;br /&gt;
&lt;br /&gt;
Во-первых, добавив защитный символ '''$''', получив строку '''ababbbaa$''' (для этого алгоритма он не требуется, но может понадобиться в применениях суффиксного массива). Во-вторых, дополним ее до четной длины, получив '''ababbbaa$$'''.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для нечетных суффиксов'''&lt;br /&gt;
# В новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет четыре элемента — '''ba''', '''bb''', '''a$''', '''$$'''. После сортировки они получат номера 2, 3, 1 и 0 соответственно.&lt;br /&gt;
# Переводим строку S[1..n] = '''babbbaa$$$''' в новый алфавит. Сжатой строкой &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; будет '''23210'''.&lt;br /&gt;
# После рекурсивного вызова получим, что &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [4, 3, 2, 0, 1], и &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; = [9, 7, 5, 1, 3].&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для четных суффиксов'''&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''$''', 9), ('''a''', 7), ('''b''', 5), ('''a''', 1), ('''a''', 3)].&lt;br /&gt;
# После сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt;= [('''$''', 9), ('''a''', 7), ('''a''', 1), ('''a''', 3), ('''b''', 5)].&lt;br /&gt;
# Восстановив массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;, получаем [8, 6, 0, 2, 4], что действительно является суффиксным массивом для четных суффиксов.&lt;br /&gt;
&lt;br /&gt;
* '''Слияние суффиксных массивов'''&lt;br /&gt;
Если бы мы умели сливать &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; за линейное время, получили бы:&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 9 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; a\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; aa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ababbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; baa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; babbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Как и было сказано вначале, избавиться от лишних '''$''' легко, так как суффиксы, им соответствующие, будут первыми в суффиксном массиве (в данном случае достаточно выбросить &amp;quot;9&amp;quot; из суффиксного массива).&lt;br /&gt;
&lt;br /&gt;
== Алгоритм Карккайнена-Сандерса == &lt;br /&gt;
Изменим изначальный алгоритм следующим образом:&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих позициям, не кратным трем. Рекурсивно сведем это к построению суффиксного массива для строки длиной в две трети исходной.&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих кратным трем позициям, используя результат первого шага, за линейное время.&lt;br /&gt;
# Сольем эти суффиксные массивы в один за линейное время.&lt;br /&gt;
&lt;br /&gt;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left(\dfrac23 n\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого также является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt; (это видно из того, что сумма геометрической прогрессии с основанием &amp;lt;tex&amp;gt; \dfrac23 &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Аналогично первой версии алгоритма, дополним строку &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; до длины, кратной трем, защитными символами &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* '''База рекурсии'''&lt;br /&gt;
Для этого алгоритма минимальной базой рекурсии будет строка длиной 4, так как она дополняется до длины 6, после чего вновь следует рекурсивный вызов строки длиной 4, и, если бы база была меньше 4, алгоритм вошел бы в бесконечную рекурсию. На этом этапе также можно применить обычную сортировку суффиксов, так как это потребует &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
На этом шаге строится суффиксный массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; для множества суффиксов &amp;lt;tex&amp;gt; \{ S[i..n-1] | i \bmod 3 \ne 0 \} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Получим строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; аналогично предыдущему алгоритму:&lt;br /&gt;
#* Сделаем список, состоящий из троек &amp;lt;tex&amp;gt; S[i..i+2]&amp;lt;/tex&amp;gt; , где &amp;lt;tex&amp;gt; i \bmod 3 \ne 0 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его за линейное время цифровой сортировкой и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S[1..n]S[2..n+1] &amp;lt;/tex&amp;gt; в строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; длиной &amp;lt;tex&amp;gt; \dfrac23 n &amp;lt;/tex&amp;gt; в алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;. Тогда суффиксу &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt; в старом алфавите, где &amp;lt;tex&amp;gt; i \bmod 3 = 1 &amp;lt;/tex&amp;gt;, в новом алфавите будет соответствовать строка &amp;lt;tex&amp;gt; S'\left[\dfrac{i-1}{3}..\dfrac{n}{3} - 1\right] &amp;lt;/tex&amp;gt;, а если &amp;lt;tex&amp;gt; i\bmod 3 = 2 &amp;lt;/tex&amp;gt;, то строка &amp;lt;tex&amp;gt; S'\left[\dfrac{n}{3} + \dfrac{i-2}{3}..\dfrac{2n}{3} - 1\right] &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Вызовем алгоритм рекурсивно для строки &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt;, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Пройдем по массиву &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt; A_{S'}[i] &amp;lt; \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j = 3A_{S'}[i] + 1 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;, если же &amp;lt;tex&amp;gt; A_{S'}[i] \geqslant \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j =  3\left(A_{S'}[i] - \dfrac{n}{3}\right) + 2 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;. Псевдокод получения &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;.length - 1:&lt;br /&gt;
    '''if''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] &amp;lt; n/3:&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] + 1)&lt;br /&gt;
    '''else''':&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * (&amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] - n/3) + 2)&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
Этот шаг также аналогичен первой версии алгоритма. Сортировка множества &amp;lt;tex&amp;gt; \{ S[i..n-1] | i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке пар &amp;lt;tex&amp;gt; \{ (S[i], S[i+1..n-1]) | i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; S[i+1..n-1] &amp;lt;/tex&amp;gt; — суффиксы в позициях, равных 1 по модулю 3, относительный порядок которых уже известен. Выпишем эти пары в порядке вхождения их в &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и отсортируем по первому элементу устойчивой сортировкой подсчетом, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;. Псевдокод этого шага:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' 2n/3 - 1:&lt;br /&gt;
     '''if''' &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[i] % 3 == 1:&lt;br /&gt;
         M.add(Pair(S[&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 '''for''' i = 0 '''to''' n/3 - 1:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&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; A_{S_0} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, чтобы получить суффиксный массив &amp;lt;tex&amp;gt; A_{S} &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;
&lt;br /&gt;
Пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 1 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Это аналогично сравнению пар &amp;lt;tex&amp;gt; (S[i], S[i+1..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S[j], S[j+1..n-1]) &amp;lt;/tex&amp;gt;. Сравнить первые элементы пар мы можем за &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt;, а относительный порядок вторых элементов пар нам уже известен, так как они соответствуют позициям, равным 2 и 1 по модулю 3 соответственно.&lt;br /&gt;
&lt;br /&gt;
Аналогично, пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 2 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Тогда это аналогично сравнению троек &amp;lt;tex&amp;gt; (S[i], S[i+1], S[i+2..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S[j], S[j+1], S[j+2..n-1]) &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;A_{S}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 // Вначале предподсчитаем за O(n) обратную перестановку для суффиксного массива &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;, то есть массив rank такой, что &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[rank[i]] = i.&lt;br /&gt;
 // Тогда мы сможем за O(1) сравнивать суффиксы по их позиции.&lt;br /&gt;
 rank = inverse(&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;) &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3 '''and''' j &amp;lt; n/3:&lt;br /&gt;
     pos12 = &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i]&lt;br /&gt;
     pos0  = &amp;lt;tex&amp;gt; A_{0} &amp;lt;/tex&amp;gt;[j]&lt;br /&gt;
     '''if''' pos12 % 3 == 1:&lt;br /&gt;
        ''' if''' Pair(S[pos12], rank[pos12 + 1]) &amp;lt; Pair(S[pos0], rank[pos0 + 1]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++  &lt;br /&gt;
     '''else''':&lt;br /&gt;
         '''if''' Triple(S[pos12], S[pos12 + 1], rank[pos12 + 2]) &amp;lt; Triple(S[pos0], S[pos0 + 1], rank[pos0 + 2]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++ &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i])&lt;br /&gt;
     i++&lt;br /&gt;
 '''while''' j &amp;lt; n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{0}} &amp;lt;/tex&amp;gt;[j])&lt;br /&gt;
     j++&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;
Построим суффиксный массив для строки '''abbacab'''. После добавления защитного символа и дополнения до кратной трем длины, получим '''abbacab$$'''.&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
# Тройками, соответствующими равными 1 по модулю 3 позициям, будут: '''bba''', '''cab''', '''$$$''', соответствующими равным 2 по модулю 3 — '''bac''', '''cab$''', '''$$$'''. Новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет содержать элементы '''bba''', '''cab''', '''$$$''', '''bac''', '''ab$''', которые после сортировки получат номера 3, 4, 0, 2, 1 соответственно.&lt;br /&gt;
# Строкой '''bbacab$$$bacab$$$$''' в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; = 340210. &lt;br /&gt;
# После рекурсивного вызова получим &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [5, 2, 4, 3, 0, 1]. Пересчитав &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, получим [(5 - 3)*3 + 2, 2 * 3 + 1, (4 - 3) * 3 + 2, (3 - 3) * 3 + 2, 0 * 3 + 1, 1 * 3 + 1] = [8, 7, 5, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и выбрав в нем элементы, равные 1 по модулю 3, получим массив пар &amp;lt;tex&amp;gt;M&amp;lt;/tex&amp;gt; = [(&amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, 7), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 1), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 4)]&lt;br /&gt;
# После устойчивой сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''a''', 1), ('''a''', 4), ('''b''', 7)]&lt;br /&gt;
# Восстановив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;, получаем [0, 3, 6].&lt;br /&gt;
* '''Слияние суффиксных массивов'''&lt;br /&gt;
Рассмотрим, к примеру, третью итерацию слияния, к этой итерации массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 2, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0, на ней мы сливаем суффиксы, соответствующие позициям 5 и 0. &lt;br /&gt;
# Образуем тройки (S[5], S[6], S[7..8]) и (S[0], S[1], S[2..8]).&lt;br /&gt;
# После получения относительного порядка суффиксов, получим тройки ('''a''', '''b''', 1) и ('''a''', '''b''', 3). Первая тройка меньше второй, поэтому добавляем суффикс, соответствующий позиции 5 в массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# В конце итерации получаем &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 3, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0.&lt;br /&gt;
К концу слияния получим &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5, 0, 3, 6, 2, 1, 4]. Так как мы добавляли один символ &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; в начале алгоритма для дополнения строки до длины, кратной трем, выбросим последний суффикс из &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;, получим в итоге, что &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [7, 5, 0, 3, 6, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; acab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; b\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; cab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
Заметим, что №9 будет выброшен, так как в начале алгоритма был добавлен один &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt; к строке&lt;br /&gt;
 &lt;br /&gt;
{|&lt;br /&gt;
| [[Файл:Kark_sanders_stage1.png|325px|thumb| Фаза 1]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage2.png|325px|thumb| Фаза 2]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage3.png|325px|thumb| Фаза 3]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Получение массива LCP ==&lt;br /&gt;
Массив LCP можно получить за линейное время [[Алгоритм_Касаи_и_др. | алгоритмом Касаи]].&lt;br /&gt;
&lt;br /&gt;
== Обобщение алгоритма ==&lt;br /&gt;
На самом деле, алгоритм можно обобщить&amp;lt;ref name=&amp;quot;generalisation&amp;quot;&amp;gt; Juha Kärkkäinen, Peter Sanders and Stefan Burkhardt. Linear work suffix array construction. http://www.cs.helsinki.fi/juha.karkkainen/publications/jacm05-revised.pdf &amp;lt;/ref&amp;gt;, взяв на первом шаге, к примеру, суффиксы, позиции которых по модулю 7 дают 3, 5 и 6. Для этого потребуются некоторое усложнение алгоритма, например, сортировка оставшихся суффиксов в нескольких группах на шаге 2 и слияние нескольких групп на шаге 3, но основная идея алгоритма остается той же. Множества, которые можно выбрать, на первом шаге определяются '''разностным покрытием''' (''difference cover'').&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Разностное покрытие''' (''difference cover'') &amp;lt;tex&amp;gt; D &amp;lt;/tex&amp;gt; по модулю &amp;lt;tex&amp;gt;m &amp;lt;/tex&amp;gt; — множество чисел от &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;m - 1 &amp;lt;/tex&amp;gt; таких, что &amp;lt;tex&amp;gt; \forall i \in [0, m-1]: \exists j, k \in D: i \equiv k - j \pmod m &amp;lt;/tex&amp;gt;.&lt;br /&gt;
}} &lt;br /&gt;
Например, &amp;lt;tex&amp;gt; \{1, 2\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt; \{3, 5, 6\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt; \{1\} &amp;lt;/tex&amp;gt; — не является разностным покрытием по модулю &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;, поэтому этот алгоритм не применим к нему. Подробнее узнать, как вычислять разностное покрытие для заданного модуля можно также здесь&amp;lt;ref name=&amp;quot;generalisation&amp;quot;/&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Источники информации==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
* [http://www.cs.helsinki.fi/juha.karkkainen/publications/icalp03.pdf Juha Kärkkäinen and Peter Sanders. Simple linear work suffix array construction]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Суффиксный массив]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=48191</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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=48191"/>
				<updated>2015-06-09T05:49:38Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Алгоритм «разделяй и властвуй» */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Алгоритм Карккайнена-Сандерса''' (Kärkkäinen, Sanders) — алгоритм построения [[суффиксный массив | суффиксного массива]] за линейное время.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Используемые обозначения ==&lt;br /&gt;
* В данном конспекте используется 0-индексация.&lt;br /&gt;
* &amp;lt;tex&amp;gt;S[i..j] &amp;lt;/tex&amp;gt; — подстрока строки &amp;lt;tex&amp;gt; S &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;-й символы включительно.&lt;br /&gt;
* Пусть длина строки &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;. Обозначим &amp;lt;tex&amp;gt; S^*[i..j] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; j \geqslant n &amp;lt;/tex&amp;gt; как строку &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt;, дополненную защитными символами &amp;lt;tex&amp;gt; \$ &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;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Четным суффиксом''' назовем суффикс, начинающийся в четной позиции. &amp;lt;br&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;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left( \dfrac{n}{2}\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для упрощения алгоритма вначале дополним нашу строку до четной длины (например, добавлением &amp;lt;tex&amp;gt;$&amp;lt;/tex&amp;gt; в конец). На шаге слияния мы сможем избавиться от него.&lt;br /&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;
&lt;br /&gt;
# Отобразим исходную строку &amp;lt;tex&amp;gt; S &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; длины  &amp;lt;tex&amp;gt; \dfrac{n}{2} &amp;lt;/tex&amp;gt;  следующим образом:&lt;br /&gt;
#* Сделаем список, состоящий из пар символов вида &amp;lt;tex&amp;gt; S[i..i + 1] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; i \bmod 2 = 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его цифровой сортировкой за линейное время и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S[1..n] &amp;lt;/tex&amp;gt; в алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;, получив строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; половинной длины.&lt;br /&gt;
# Рекурсивно построим суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Построим суффиксный массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;. Очевидно, &amp;lt;tex&amp;gt; A_{S_o}[i] = 2 A_{S'}[i] + 1 &amp;lt;/tex&amp;gt;, так отношение упорядоченности любых двух строк в старом алфавите &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt; эквивалентно отношению упорядоченности в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; по его построению.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для четных суффиксов'''&lt;br /&gt;
На этом шаге мы за линейное время получим суффиксный массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; для четных суффиксов, используя уже построенный &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что сортировка множества четных суффиксов &amp;lt;tex&amp;gt; \{ S[i..n] | i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке множества пар &amp;lt;tex&amp;gt; \{ (S[i], S[i+1..n]) | i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt;. Однако &amp;lt;tex&amp;gt; S[i+1..n] &amp;lt;/tex&amp;gt; — нечетный суффикс, и его относительную позицию мы уже узнали на шаге 1.&lt;br /&gt;
&lt;br /&gt;
Таким образом, чтобы отсортировать эти пары за линейное время, сначала сразу выпишем их в порядке возрастания второго элемента пары (то есть в порядке вхождения в массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;), а потом отсортируем устойчивой сортировкой подсчетом по первым элементам. После этого легко можно восстановить массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
     M.add(Pair(S[&amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
    &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&gt;
&lt;br /&gt;
Заметим, что массив &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; перед сортировкой подсчетом не был явно отсортирован по вторым элементам, и хранил не суффиксы, а их позиции в строке &amp;lt;tex&amp;gt; S &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;
Для суффиксного дерева третий шаг алгоритма опирается на специфические особенности суффиксных деревьев, которые не присущи суффиксным массивам&amp;lt;ref&amp;gt;M. Farach. Optimal suffix tree construction with large alphabets. http://www.cs.rutgers.edu/~farach/pubs/FarFerrMuthu00.pdf &amp;lt;/ref&amp;gt; .  &lt;br /&gt;
В случае суффиксного массива слияние становится очень сложным, но все же оно было реализовано в алгоритме Ким-Сим-Парк-Парка&amp;lt;ref&amp;gt; D. K. Kim, J. S. Sim, H. Park, and K. Park. Linear-time construction of suffix arrays. http://www.springerlink.com/content/568156021q45r320/&amp;lt;/ref&amp;gt;. Однако простой модификацией алгоритма можно значительно упростить его.&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
Покажем первые два шага агоритма для строки '''ababbbaa'''.&lt;br /&gt;
&lt;br /&gt;
Во-первых, добавив защитный символ '''$''', получив строку '''ababbbaa$''' (для этого алгоритма он не требуется, но может понадобиться в применениях суффиксного массива). Во-вторых, дополним ее до четной длины, получив '''ababbbaa$$'''.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для нечетных суффиксов'''&lt;br /&gt;
# В новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет четыре элемента — '''ba''', '''bb''', '''a$''', '''$$'''. После сортировки они получат номера 2, 3, 1 и 0 соответственно.&lt;br /&gt;
# Переводим строку S[1..n] = '''babbbaa$$$''' в новый алфавит. Сжатой строкой &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; будет '''23210'''.&lt;br /&gt;
# После рекурсивного вызова получим, что &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [4, 3, 2, 0, 1], и &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; = [9, 7, 5, 1, 3].&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для четных суффиксов'''&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''$''', 9), ('''a''', 7), ('''b''', 5), ('''a''', 1), ('''a''', 3)].&lt;br /&gt;
# После сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt;= [('''$''', 9), ('''a''', 7), ('''a''', 1), ('''a''', 3), ('''b''', 5)].&lt;br /&gt;
# Восстановив массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;, получаем [8, 6, 0, 2, 4], что действительно является суффиксным массивом для четных суффиксов.&lt;br /&gt;
&lt;br /&gt;
* '''Слияние суффиксных массивов'''&lt;br /&gt;
Если бы мы умели сливать &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; за линейное время, получили бы:&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 9 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; a\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; aa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ababbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; baa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; babbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Как и было сказано вначале, избавиться от лишних '''$''' легко, так как суффиксы, им соответствующие, будут первыми в суффиксном массиве (в данном случае достаточно выбросить &amp;quot;9&amp;quot; из суффиксного массива).&lt;br /&gt;
&lt;br /&gt;
== Алгоритм Карккайнена-Сандерса == &lt;br /&gt;
Изменим изначальный алгоритм следующим образом:&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих позициям, не кратным трем. Рекурсивно сведем это к построению суффиксного массива для строки длиной в две трети исходной.&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих кратным трем позициям, используя результат первого шага, за линейное время.&lt;br /&gt;
# Сольем эти суффиксные массивы в один за линейное время.&lt;br /&gt;
&lt;br /&gt;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left(\dfrac23 n\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого также является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt; (это видно из того, что сумма геометрической прогрессии с основанием &amp;lt;tex&amp;gt; \dfrac23 &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Аналогично первой версии алгоритма, дополним строку &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; до длины, кратной трем, защитными символами &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* '''База рекурсии'''&lt;br /&gt;
Для этого алгоритма минимальной базой рекурсии будет строка длиной 4, так как она дополняется до длины 6, после чего вновь следует рекурсивный вызов строки длиной 4, и, если бы база была меньше 4, алгоритм вошел бы в бесконечную рекурсию. На этом этапе также можно применить обычную сортировку суффиксов, так как это потребует &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
На этом шаге строится суффиксный массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; для множества суффиксов &amp;lt;tex&amp;gt; \{ S[i..n-1] | i \bmod 3 \ne 0 \} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Получим строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; аналогично предыдущему алгоритму:&lt;br /&gt;
#* Сделаем список, состоящий из троек &amp;lt;tex&amp;gt; S[i..i+2]&amp;lt;/tex&amp;gt; , где &amp;lt;tex&amp;gt; i \bmod 3 \ne 0 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его за линейное время цифровой сортировкой и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S[1..n]S[2..n+1] &amp;lt;/tex&amp;gt; в строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; длиной &amp;lt;tex&amp;gt; \dfrac23 n &amp;lt;/tex&amp;gt; в алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;. Тогда суффиксу &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt; в старом алфавите, где &amp;lt;tex&amp;gt; i \bmod 3 = 1 &amp;lt;/tex&amp;gt;, в новом алфавите будет соответствовать строка &amp;lt;tex&amp;gt; S'\left[\dfrac{i-1}{3}..\dfrac{n}{3} - 1\right] &amp;lt;/tex&amp;gt;, а если &amp;lt;tex&amp;gt; i\bmod 3 = 2 &amp;lt;/tex&amp;gt;, то строка &amp;lt;tex&amp;gt; S'\left[\dfrac{n}{3} + \dfrac{i-2}{3}..\dfrac{2n}{3} - 1\right] &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Вызовем алгоритм рекурсивно для строки &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt;, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Пройдем по массиву &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt; A_{S'}[i] &amp;lt; \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j = 3A_{S'}[i] + 1 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;, если же &amp;lt;tex&amp;gt; A_{S'}[i] \geqslant \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j =  3\left(A_{S'}[i] - \dfrac{n}{3}\right) + 2 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;. Псевдокод получения &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;.length - 1:&lt;br /&gt;
    '''if''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] &amp;lt; n/3:&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] + 1)&lt;br /&gt;
    '''else''':&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * (&amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] - n/3) + 2)&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
Этот шаг также аналогичен первой версии алгоритма. Сортировка множества &amp;lt;tex&amp;gt; \{ S[i..n-1] | i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке пар &amp;lt;tex&amp;gt; \{ (S[i], S[i+1..n-1]) | i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; S[i+1..n-1] &amp;lt;/tex&amp;gt; — суффиксы в позициях, равных 1 по модулю 3, относительный порядок которых уже известен. Выпишем эти пары в порядке вхождения их в &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и отсортируем по первому элементу устойчивой сортировкой подсчетом, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;. Псевдокод этого шага:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' 2n/3 - 1:&lt;br /&gt;
     '''if''' &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[i] % 3 == 1:&lt;br /&gt;
         M.add(Pair(S[&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 '''for''' i = 0 '''to''' n/3 - 1:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&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; A_{S_0} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, чтобы получить суффиксный массив &amp;lt;tex&amp;gt; A_{S} &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;
&lt;br /&gt;
Пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 1 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Это аналогично сравнению пар &amp;lt;tex&amp;gt; (S[i], S[i+1..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S[j], S[j+1..n-1]) &amp;lt;/tex&amp;gt;. Сравнить первые элементы пар мы можем за &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt;, а относительный порядок вторых элементов пар нам уже известен, так как они соответствуют позициям, равным 2 и 1 по модулю 3 соответственно.&lt;br /&gt;
&lt;br /&gt;
Аналогично, пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 2 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Тогда это аналогично сравнению троек &amp;lt;tex&amp;gt; (S[i], S[i+1], S[i+2..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S[j], S[j+1], S[j+2..n-1]) &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;A_{S}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 // Вначале предподсчитаем за O(n) обратную перестановку для суффиксного массива &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;, то есть массив rank такой, что &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[rank[i]] = i.&lt;br /&gt;
 // Тогда мы сможем за O(1) сравнивать суффиксы по их позиции.&lt;br /&gt;
 rank = inverse(&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;) &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3 '''and''' j &amp;lt; n/3:&lt;br /&gt;
     pos12 = &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i]&lt;br /&gt;
     pos0  = &amp;lt;tex&amp;gt; A_{0} &amp;lt;/tex&amp;gt;[j]&lt;br /&gt;
     '''if''' pos12 % 3 == 1:&lt;br /&gt;
        ''' if''' Pair(S[pos12], rank[pos12 + 1]) &amp;lt; Pair(S[pos0], rank[pos0 + 1]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++  &lt;br /&gt;
     '''else''':&lt;br /&gt;
         '''if''' Triple(S[pos12], S[pos12 + 1], rank[pos12 + 2]) &amp;lt; Triple(S[pos0], S[pos0 + 1], rank[pos0 + 2]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++ &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i])&lt;br /&gt;
     i++&lt;br /&gt;
 '''while''' j &amp;lt; n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{0}} &amp;lt;/tex&amp;gt;[j])&lt;br /&gt;
     j++&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;
Построим суффиксный массив для строки '''abbacab'''. После добавления защитного символа и дополнения до кратной трем длины, получим '''abbacab$$'''.&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
# Тройками, соответствующими равными 1 по модулю 3 позициям, будут: '''bba''', '''cab''', '''$$$''', соответствующими равным 2 по модулю 3 — '''bac''', '''cab$''', '''$$$'''. Новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет содержать элементы '''bba''', '''cab''', '''$$$''', '''bac''', '''ab$''', которые после сортировки получат номера 3, 4, 0, 2, 1 соответственно.&lt;br /&gt;
# Строкой '''bbacab$$$bacab$$$$''' в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; = 340210. &lt;br /&gt;
# После рекурсивного вызова получим &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [5, 2, 4, 3, 0, 1]. Пересчитав &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, получим [(5 - 3)*3 + 2, 2 * 3 + 1, (4 - 3) * 3 + 2, (3 - 3) * 3 + 2, 0 * 3 + 1, 1 * 3 + 1] = [8, 7, 5, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и выбрав в нем элементы, равные 1 по модулю 3, получим массив пар &amp;lt;tex&amp;gt;M&amp;lt;/tex&amp;gt; = [(&amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, 7), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 1), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 4)]&lt;br /&gt;
# После устойчивой сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''a''', 1), ('''a''', 4), ('''b''', 7)]&lt;br /&gt;
# Восстановив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;, получаем [0, 3, 6].&lt;br /&gt;
* '''Слияние суффиксных массивов'''&lt;br /&gt;
Рассмотрим, к примеру, третью итерацию слияния, к этой итерации массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 2, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0, на ней мы сливаем суффиксы, соответствующие позициям 5 и 0. &lt;br /&gt;
# Образуем тройки (S[5], S[6], S[7..8]) и (S[0], S[1], S[2..8]).&lt;br /&gt;
# После получения относительного порядка суффиксов, получим тройки ('''a''', '''b''', 1) и ('''a''', '''b''', 3). Первая тройка меньше второй, поэтому добавляем суффикс, соответствующий позиции 5 в массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# В конце итерации получаем &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 3, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0.&lt;br /&gt;
К концу слияния получим &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5, 0, 3, 6, 2, 1, 4]. Так как мы добавляли один символ &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; в начале алгоритма для дополнения строки до длины, кратной трем, выбросим последний суффикс из &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;, получим в итоге, что &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [7, 5, 0, 3, 6, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; acab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; b\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; cab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
Заметим, что №9 будет выброшен, так как в начале алгоритма был добавлен один &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt; к строке&lt;br /&gt;
 &lt;br /&gt;
{|&lt;br /&gt;
| [[Файл:Kark_sanders_stage1.png|325px|thumb| Фаза 1]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage2.png|325px|thumb| Фаза 2]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage3.png|325px|thumb| Фаза 3]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Получение массива LCP ==&lt;br /&gt;
Массив LCP можно получить за линейное время [[Алгоритм_Касаи_и_др. | алгоритмом Касаи]].&lt;br /&gt;
&lt;br /&gt;
== Обобщение алгоритма ==&lt;br /&gt;
На самом деле, алгоритм можно обобщить&amp;lt;ref name=&amp;quot;generalisation&amp;quot;&amp;gt; Juha Kärkkäinen, Peter Sanders and Stefan Burkhardt. Linear work suffix array construction. http://www.cs.helsinki.fi/juha.karkkainen/publications/jacm05-revised.pdf &amp;lt;/ref&amp;gt;, взяв на первом шаге, к примеру, суффиксы, позиции которых по модулю 7 дают 3, 5 и 6. Для этого потребуются некоторое усложнение алгоритма, например, сортировка оставшихся суффиксов в нескольких группах на шаге 2 и слияние нескольких групп на шаге 3, но основная идея алгоритма остается той же. Множества, которые можно выбрать, на первом шаге определяются '''разностным покрытием''' (''difference cover'').&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Разностное покрытие''' (''difference cover'') &amp;lt;tex&amp;gt; D &amp;lt;/tex&amp;gt; по модулю &amp;lt;tex&amp;gt;m &amp;lt;/tex&amp;gt; — множество чисел от &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;m - 1 &amp;lt;/tex&amp;gt; таких, что &amp;lt;tex&amp;gt; \forall i \in [0, m-1]: \exists j, k \in D: i \equiv k - j \pmod m &amp;lt;/tex&amp;gt;.&lt;br /&gt;
}} &lt;br /&gt;
Например, &amp;lt;tex&amp;gt; \{1, 2\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt; \{3, 5, 6\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt; \{1\} &amp;lt;/tex&amp;gt; — не является разностным покрытием по модулю &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;, поэтому этот алгоритм не применим к нему. Подробнее узнать, как вычислять разностное покрытие для заданного модуля можно также здесь&amp;lt;ref name=&amp;quot;generalisation&amp;quot;/&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Источники информации==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
* [http://www.cs.helsinki.fi/juha.karkkainen/publications/icalp03.pdf Juha Kärkkäinen and Peter Sanders. Simple linear work suffix array construction]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Суффиксный массив]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=48190</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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=48190"/>
				<updated>2015-06-09T05:48:40Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Используемые обозначения */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Алгоритм Карккайнена-Сандерса''' (Kärkkäinen, Sanders) — алгоритм построения [[суффиксный массив | суффиксного массива]] за линейное время.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Используемые обозначения ==&lt;br /&gt;
* В данном конспекте используется 0-индексация.&lt;br /&gt;
* &amp;lt;tex&amp;gt;S[i..j] &amp;lt;/tex&amp;gt; — подстрока строки &amp;lt;tex&amp;gt; S &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;-й символы включительно.&lt;br /&gt;
* Пусть длина строки &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;. Обозначим &amp;lt;tex&amp;gt; S^*[i..j] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; j \geqslant n &amp;lt;/tex&amp;gt; как строку &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt;, дополненную защитными символами &amp;lt;tex&amp;gt; \$ &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;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Четным суффиксом''' назовем суффикс, начинающийся в четной позиции. &amp;lt;br&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;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left( \dfrac{n}{2}\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого является &amp;lt;tex&amp;gt; T(n) = O(n) &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;
&lt;br /&gt;
* '''Суффиксный массив для нечетных суффиксов'''&lt;br /&gt;
&lt;br /&gt;
# Отобразим исходную строку &amp;lt;tex&amp;gt; S &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; длины  &amp;lt;tex&amp;gt; \dfrac{n}{2} &amp;lt;/tex&amp;gt;  следующим образом:&lt;br /&gt;
#* Сделаем список, состоящий из пар символов вида &amp;lt;tex&amp;gt; S[i..i + 1] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; i \bmod 2 = 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его цифровой сортировкой за линейное время и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S[1..n] &amp;lt;/tex&amp;gt; в алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;, получив строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; половинной длины.&lt;br /&gt;
# Рекурсивно построим суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Построим суффиксный массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;. Очевидно, &amp;lt;tex&amp;gt; A_{S_o}[i] = 2 A_{S'}[i] + 1 &amp;lt;/tex&amp;gt;, так отношение упорядоченности любых двух строк в старом алфавите &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt; эквивалентно отношению упорядоченности в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; по его построению.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для четных суффиксов'''&lt;br /&gt;
На этом шаге мы за линейное время получим суффиксный массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; для четных суффиксов, используя уже построенный &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что сортировка множества четных суффиксов &amp;lt;tex&amp;gt; \{ S[i..n] | i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке множества пар &amp;lt;tex&amp;gt; \{ (S[i], S[i+1..n]) | i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt;. Однако &amp;lt;tex&amp;gt; S[i+1..n] &amp;lt;/tex&amp;gt; — нечетный суффикс, и его относительную позицию мы уже узнали на шаге 1.&lt;br /&gt;
&lt;br /&gt;
Таким образом, чтобы отсортировать эти пары за линейное время, сначала сразу выпишем их в порядке возрастания второго элемента пары (то есть в порядке вхождения в массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;), а потом отсортируем устойчивой сортировкой подсчетом по первым элементам. После этого легко можно восстановить массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
     M.add(Pair(S[&amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
    &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&gt;
&lt;br /&gt;
Заметим, что массив &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; перед сортировкой подсчетом не был явно отсортирован по вторым элементам, и хранил не суффиксы, а их позиции в строке &amp;lt;tex&amp;gt; S &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;
Для суффиксного дерева третий шаг алгоритма опирается на специфические особенности суффиксных деревьев, которые не присущи суффиксным массивам&amp;lt;ref&amp;gt;M. Farach. Optimal suffix tree construction with large alphabets. http://www.cs.rutgers.edu/~farach/pubs/FarFerrMuthu00.pdf &amp;lt;/ref&amp;gt; .  &lt;br /&gt;
В случае суффиксного массива слияние становится очень сложным, но все же оно было реализовано в алгоритме Ким-Сим-Парк-Парка&amp;lt;ref&amp;gt; D. K. Kim, J. S. Sim, H. Park, and K. Park. Linear-time construction of suffix arrays. http://www.springerlink.com/content/568156021q45r320/&amp;lt;/ref&amp;gt;. Однако простой модификацией алгоритма можно значительно упростить его.&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
Покажем первые два шага агоритма для строки '''ababbbaa'''.&lt;br /&gt;
&lt;br /&gt;
Во-первых, добавив защитный символ '''$''', получив строку '''ababbbaa$''' (для этого алгоритма он не требуется, но может понадобиться в применениях суффиксного массива). Во-вторых, дополним ее до четной длины, получив '''ababbbaa$$'''.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для нечетных суффиксов'''&lt;br /&gt;
# В новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет четыре элемента — '''ba''', '''bb''', '''a$''', '''$$'''. После сортировки они получат номера 2, 3, 1 и 0 соответственно.&lt;br /&gt;
# Переводим строку S[1..n] = '''babbbaa$$$''' в новый алфавит. Сжатой строкой &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; будет '''23210'''.&lt;br /&gt;
# После рекурсивного вызова получим, что &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [4, 3, 2, 0, 1], и &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; = [9, 7, 5, 1, 3].&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для четных суффиксов'''&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''$''', 9), ('''a''', 7), ('''b''', 5), ('''a''', 1), ('''a''', 3)].&lt;br /&gt;
# После сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt;= [('''$''', 9), ('''a''', 7), ('''a''', 1), ('''a''', 3), ('''b''', 5)].&lt;br /&gt;
# Восстановив массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;, получаем [8, 6, 0, 2, 4], что действительно является суффиксным массивом для четных суффиксов.&lt;br /&gt;
&lt;br /&gt;
* '''Слияние суффиксных массивов'''&lt;br /&gt;
Если бы мы умели сливать &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; за линейное время, получили бы:&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 9 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; a\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; aa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ababbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; baa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; babbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Как и было сказано вначале, избавиться от лишних '''$''' легко, так как суффиксы, им соответствующие, будут первыми в суффиксном массиве (в данном случае достаточно выбросить &amp;quot;9&amp;quot; из суффиксного массива).&lt;br /&gt;
&lt;br /&gt;
== Алгоритм Карккайнена-Сандерса == &lt;br /&gt;
Изменим изначальный алгоритм следующим образом:&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих позициям, не кратным трем. Рекурсивно сведем это к построению суффиксного массива для строки длиной в две трети исходной.&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих кратным трем позициям, используя результат первого шага, за линейное время.&lt;br /&gt;
# Сольем эти суффиксные массивы в один за линейное время.&lt;br /&gt;
&lt;br /&gt;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left(\dfrac23 n\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого также является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt; (это видно из того, что сумма геометрической прогрессии с основанием &amp;lt;tex&amp;gt; \dfrac23 &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Аналогично первой версии алгоритма, дополним строку &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; до длины, кратной трем, защитными символами &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* '''База рекурсии'''&lt;br /&gt;
Для этого алгоритма минимальной базой рекурсии будет строка длиной 4, так как она дополняется до длины 6, после чего вновь следует рекурсивный вызов строки длиной 4, и, если бы база была меньше 4, алгоритм вошел бы в бесконечную рекурсию. На этом этапе также можно применить обычную сортировку суффиксов, так как это потребует &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
На этом шаге строится суффиксный массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; для множества суффиксов &amp;lt;tex&amp;gt; \{ S[i..n-1] | i \bmod 3 \ne 0 \} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Получим строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; аналогично предыдущему алгоритму:&lt;br /&gt;
#* Сделаем список, состоящий из троек &amp;lt;tex&amp;gt; S[i..i+2]&amp;lt;/tex&amp;gt; , где &amp;lt;tex&amp;gt; i \bmod 3 \ne 0 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его за линейное время цифровой сортировкой и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S[1..n]S[2..n+1] &amp;lt;/tex&amp;gt; в строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; длиной &amp;lt;tex&amp;gt; \dfrac23 n &amp;lt;/tex&amp;gt; в алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;. Тогда суффиксу &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt; в старом алфавите, где &amp;lt;tex&amp;gt; i \bmod 3 = 1 &amp;lt;/tex&amp;gt;, в новом алфавите будет соответствовать строка &amp;lt;tex&amp;gt; S'\left[\dfrac{i-1}{3}..\dfrac{n}{3} - 1\right] &amp;lt;/tex&amp;gt;, а если &amp;lt;tex&amp;gt; i\bmod 3 = 2 &amp;lt;/tex&amp;gt;, то строка &amp;lt;tex&amp;gt; S'\left[\dfrac{n}{3} + \dfrac{i-2}{3}..\dfrac{2n}{3} - 1\right] &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Вызовем алгоритм рекурсивно для строки &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt;, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Пройдем по массиву &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt; A_{S'}[i] &amp;lt; \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j = 3A_{S'}[i] + 1 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;, если же &amp;lt;tex&amp;gt; A_{S'}[i] \geqslant \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j =  3\left(A_{S'}[i] - \dfrac{n}{3}\right) + 2 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;. Псевдокод получения &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;.length - 1:&lt;br /&gt;
    '''if''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] &amp;lt; n/3:&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] + 1)&lt;br /&gt;
    '''else''':&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * (&amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] - n/3) + 2)&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
Этот шаг также аналогичен первой версии алгоритма. Сортировка множества &amp;lt;tex&amp;gt; \{ S[i..n-1] | i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке пар &amp;lt;tex&amp;gt; \{ (S[i], S[i+1..n-1]) | i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; S[i+1..n-1] &amp;lt;/tex&amp;gt; — суффиксы в позициях, равных 1 по модулю 3, относительный порядок которых уже известен. Выпишем эти пары в порядке вхождения их в &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и отсортируем по первому элементу устойчивой сортировкой подсчетом, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;. Псевдокод этого шага:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' 2n/3 - 1:&lt;br /&gt;
     '''if''' &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[i] % 3 == 1:&lt;br /&gt;
         M.add(Pair(S[&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 '''for''' i = 0 '''to''' n/3 - 1:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&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; A_{S_0} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, чтобы получить суффиксный массив &amp;lt;tex&amp;gt; A_{S} &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;
&lt;br /&gt;
Пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 1 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Это аналогично сравнению пар &amp;lt;tex&amp;gt; (S[i], S[i+1..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S[j], S[j+1..n-1]) &amp;lt;/tex&amp;gt;. Сравнить первые элементы пар мы можем за &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt;, а относительный порядок вторых элементов пар нам уже известен, так как они соответствуют позициям, равным 2 и 1 по модулю 3 соответственно.&lt;br /&gt;
&lt;br /&gt;
Аналогично, пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 2 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Тогда это аналогично сравнению троек &amp;lt;tex&amp;gt; (S[i], S[i+1], S[i+2..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S[j], S[j+1], S[j+2..n-1]) &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;A_{S}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 // Вначале предподсчитаем за O(n) обратную перестановку для суффиксного массива &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;, то есть массив rank такой, что &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[rank[i]] = i.&lt;br /&gt;
 // Тогда мы сможем за O(1) сравнивать суффиксы по их позиции.&lt;br /&gt;
 rank = inverse(&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;) &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3 '''and''' j &amp;lt; n/3:&lt;br /&gt;
     pos12 = &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i]&lt;br /&gt;
     pos0  = &amp;lt;tex&amp;gt; A_{0} &amp;lt;/tex&amp;gt;[j]&lt;br /&gt;
     '''if''' pos12 % 3 == 1:&lt;br /&gt;
        ''' if''' Pair(S[pos12], rank[pos12 + 1]) &amp;lt; Pair(S[pos0], rank[pos0 + 1]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++  &lt;br /&gt;
     '''else''':&lt;br /&gt;
         '''if''' Triple(S[pos12], S[pos12 + 1], rank[pos12 + 2]) &amp;lt; Triple(S[pos0], S[pos0 + 1], rank[pos0 + 2]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++ &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i])&lt;br /&gt;
     i++&lt;br /&gt;
 '''while''' j &amp;lt; n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{0}} &amp;lt;/tex&amp;gt;[j])&lt;br /&gt;
     j++&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;
Построим суффиксный массив для строки '''abbacab'''. После добавления защитного символа и дополнения до кратной трем длины, получим '''abbacab$$'''.&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
# Тройками, соответствующими равными 1 по модулю 3 позициям, будут: '''bba''', '''cab''', '''$$$''', соответствующими равным 2 по модулю 3 — '''bac''', '''cab$''', '''$$$'''. Новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет содержать элементы '''bba''', '''cab''', '''$$$''', '''bac''', '''ab$''', которые после сортировки получат номера 3, 4, 0, 2, 1 соответственно.&lt;br /&gt;
# Строкой '''bbacab$$$bacab$$$$''' в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; = 340210. &lt;br /&gt;
# После рекурсивного вызова получим &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [5, 2, 4, 3, 0, 1]. Пересчитав &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, получим [(5 - 3)*3 + 2, 2 * 3 + 1, (4 - 3) * 3 + 2, (3 - 3) * 3 + 2, 0 * 3 + 1, 1 * 3 + 1] = [8, 7, 5, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и выбрав в нем элементы, равные 1 по модулю 3, получим массив пар &amp;lt;tex&amp;gt;M&amp;lt;/tex&amp;gt; = [(&amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, 7), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 1), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 4)]&lt;br /&gt;
# После устойчивой сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''a''', 1), ('''a''', 4), ('''b''', 7)]&lt;br /&gt;
# Восстановив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;, получаем [0, 3, 6].&lt;br /&gt;
* '''Слияние суффиксных массивов'''&lt;br /&gt;
Рассмотрим, к примеру, третью итерацию слияния, к этой итерации массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 2, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0, на ней мы сливаем суффиксы, соответствующие позициям 5 и 0. &lt;br /&gt;
# Образуем тройки (S[5], S[6], S[7..8]) и (S[0], S[1], S[2..8]).&lt;br /&gt;
# После получения относительного порядка суффиксов, получим тройки ('''a''', '''b''', 1) и ('''a''', '''b''', 3). Первая тройка меньше второй, поэтому добавляем суффикс, соответствующий позиции 5 в массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# В конце итерации получаем &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 3, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0.&lt;br /&gt;
К концу слияния получим &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5, 0, 3, 6, 2, 1, 4]. Так как мы добавляли один символ &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; в начале алгоритма для дополнения строки до длины, кратной трем, выбросим последний суффикс из &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;, получим в итоге, что &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [7, 5, 0, 3, 6, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; acab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; b\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; cab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
Заметим, что №9 будет выброшен, так как в начале алгоритма был добавлен один &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt; к строке&lt;br /&gt;
 &lt;br /&gt;
{|&lt;br /&gt;
| [[Файл:Kark_sanders_stage1.png|325px|thumb| Фаза 1]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage2.png|325px|thumb| Фаза 2]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage3.png|325px|thumb| Фаза 3]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Получение массива LCP ==&lt;br /&gt;
Массив LCP можно получить за линейное время [[Алгоритм_Касаи_и_др. | алгоритмом Касаи]].&lt;br /&gt;
&lt;br /&gt;
== Обобщение алгоритма ==&lt;br /&gt;
На самом деле, алгоритм можно обобщить&amp;lt;ref name=&amp;quot;generalisation&amp;quot;&amp;gt; Juha Kärkkäinen, Peter Sanders and Stefan Burkhardt. Linear work suffix array construction. http://www.cs.helsinki.fi/juha.karkkainen/publications/jacm05-revised.pdf &amp;lt;/ref&amp;gt;, взяв на первом шаге, к примеру, суффиксы, позиции которых по модулю 7 дают 3, 5 и 6. Для этого потребуются некоторое усложнение алгоритма, например, сортировка оставшихся суффиксов в нескольких группах на шаге 2 и слияние нескольких групп на шаге 3, но основная идея алгоритма остается той же. Множества, которые можно выбрать, на первом шаге определяются '''разностным покрытием''' (''difference cover'').&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Разностное покрытие''' (''difference cover'') &amp;lt;tex&amp;gt; D &amp;lt;/tex&amp;gt; по модулю &amp;lt;tex&amp;gt;m &amp;lt;/tex&amp;gt; — множество чисел от &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;m - 1 &amp;lt;/tex&amp;gt; таких, что &amp;lt;tex&amp;gt; \forall i \in [0, m-1]: \exists j, k \in D: i \equiv k - j \pmod m &amp;lt;/tex&amp;gt;.&lt;br /&gt;
}} &lt;br /&gt;
Например, &amp;lt;tex&amp;gt; \{1, 2\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt; \{3, 5, 6\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt; \{1\} &amp;lt;/tex&amp;gt; — не является разностным покрытием по модулю &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;, поэтому этот алгоритм не применим к нему. Подробнее узнать, как вычислять разностное покрытие для заданного модуля можно также здесь&amp;lt;ref name=&amp;quot;generalisation&amp;quot;/&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Источники информации==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
* [http://www.cs.helsinki.fi/juha.karkkainen/publications/icalp03.pdf Juha Kärkkäinen and Peter Sanders. Simple linear work suffix array construction]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Суффиксный массив]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8_LCA_%D0%BA_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B5_RMQ&amp;diff=48142</id>
		<title>Сведение задачи LCA к задаче RMQ</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8_LCA_%D0%BA_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B5_RMQ&amp;diff=48142"/>
				<updated>2015-06-08T16:10:42Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Алгоритм */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Шаблон:Задача&lt;br /&gt;
|definition = &lt;br /&gt;
Пусть дано корневое дерево &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. На вход подаются запросы вида &amp;lt;tex&amp;gt;(u,\;v)&amp;lt;/tex&amp;gt;, для каждого запроса требуется найти их наименьшего общего предка.&lt;br /&gt;
}}&lt;br /&gt;
{{Определение &lt;br /&gt;
|id=lca_suf_tree&lt;br /&gt;
|definition = '''Наименьшим общим предком''' (англ. ''least common ancestor'') двух узлов &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;T&amp;lt;/tex&amp;gt; называется узел &amp;lt;tex&amp;gt;w&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;, имеет наибольшую глубину.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Алгоритм ==&lt;br /&gt;
=== Идея ===&lt;br /&gt;
Будем решать задачу LCA, уже умея решать задачу RMQ. Тогда поиск наименьшего общего предка &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-того и &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-того элементов сводится к запросу минимума на отрезке массива, который будет введен позднее.&lt;br /&gt;
&lt;br /&gt;
=== Препроцессинг ===&lt;br /&gt;
Для каждой вершины &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; определим глубину с помощью следующей рекурсивной формулы:&lt;br /&gt;
:&amp;lt;tex&amp;gt;\mathrm{depth}(u) = \begin{cases}&lt;br /&gt;
0 &amp;amp; u = \mathrm{root}(T),\\&lt;br /&gt;
\mathrm{depth}(v) + 1 &amp;amp; u = \mathrm{son}(v).&lt;br /&gt;
\end{cases}&amp;lt;/tex&amp;gt;&lt;br /&gt;
Ясно, что глубина вершины элементарным образом поддерживается во время обхода в глубину.&lt;br /&gt;
&lt;br /&gt;
Запустим [[Обход в глубину, цвета вершин|обход в глубину]] из корня, который будет вычислять значения следующих величин: &lt;br /&gt;
#Cписок глубин посещенных вершин &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;. Глубина текущей вершины добавляется в конец списка при входе в данную вершину, а также после каждого возвращения из её сына.&lt;br /&gt;
#Список посещений узлов &amp;lt;tex&amp;gt;\mathtt{vtx}&amp;lt;/tex&amp;gt;, строящийся аналогично предыдущему, только добавляется не глубина а сама вершина.&lt;br /&gt;
#Значение функции &amp;lt;tex&amp;gt;\mathtt{I}[u]&amp;lt;/tex&amp;gt;, возвращающей любой индекс в списке глубин &amp;lt;tex&amp;gt;d&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;
Будем считать, что &amp;lt;tex&amp;gt;\mathrm{rmq}(d,l,r)&amp;lt;/tex&amp;gt; возвращает индекс минимального элемента в &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; на отрезке &amp;lt;tex&amp;gt;[l..r]&amp;lt;/tex&amp;gt;. Тогда ответом на запрос &amp;lt;tex&amp;gt;\mathrm{lca}(u, v)&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;\mathtt{I}[u] \leqslant \mathtt{I}[v]&amp;lt;/tex&amp;gt;, будет &amp;lt;tex&amp;gt;\mathtt{vtx}[\mathrm{rmq}(d,\mathtt{I}[u], \mathtt{I}[v])]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Доказательство корректности алгоритма ===&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement=&lt;br /&gt;
Наименьшему общему предку вершин &amp;lt;tex&amp;gt;u, v&amp;lt;/tex&amp;gt; соответствует минимальная глубина на отрезке &amp;lt;tex&amp;gt;d[\mathtt{I}[u], \mathtt{I}[v]]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим два узла &amp;lt;tex&amp;gt;u, v&amp;lt;/tex&amp;gt; корневого дерева &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. Рассмотрим отрезок &amp;lt;tex&amp;gt;d[\mathtt{I}[u]..\mathtt{I}[v]]&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;w&amp;lt;/tex&amp;gt; (в дереве есть только один простой путь между вершинами), а следовательно минимум на отрезке никак не больше глубины &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Заметим, что в момент добавления &amp;lt;tex&amp;gt;\mathtt{I}[u]&amp;lt;/tex&amp;gt; обход посещал поддерево с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. В момент добавления &amp;lt;tex&amp;gt;\mathtt{I}[v]&amp;lt;/tex&amp;gt; мы все еще в поддереве с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Значит, и на отрезке между &amp;lt;tex&amp;gt;\mathtt{I}[u]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathtt{I}[v]&amp;lt;/tex&amp;gt; мы находились внутри поддерева с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Отсюда сделаем заключение, что на рассматриваемом отрезке не посещалась вершина, отличная от &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;, с глубиной меньшей либо равной глубины &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;, т. к. подобной вершины нет в поддереве с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}.&lt;br /&gt;
&lt;br /&gt;
== Пример ==&lt;br /&gt;
Рассмотрим дерево на рисунке 1. Найдем наименьшего общего предка вершин, помеченных красным цветом.&lt;br /&gt;
Список глубин, получающийся в результате обхода в глубину {{---}} &amp;lt;tex&amp;gt;[0, 1, 2, 1, 2, 1, 0, 1, 0].&amp;lt;/tex&amp;gt;&lt;br /&gt;
Глубина наименьшего общего предка красных вершин равна минимуму на отрезке &amp;lt;tex&amp;gt;[2, 1, 0, 1].&amp;lt;/tex&amp;gt;&lt;br /&gt;
[[Файл:Lca to rmq.png(2).png|thumb|left|Рисунок к примеру]]&lt;br /&gt;
&amp;lt;div style='clear:left;'&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Сложность ==&lt;br /&gt;
&lt;br /&gt;
Для нахождения минимального элемента на отрезке можно использовать [[Алгоритм Фарака-Колтона и Бендера|алгоритм Фарака-Колтона и Бендера]] для &amp;lt;tex&amp;gt;\pm 1RMQ&amp;lt;/tex&amp;gt;, т. к. соседние элементы в списке глубин отличаются не более чем на единицу. Длина списка глубин составляет &amp;lt;tex&amp;gt;(2n - 1)&amp;lt;/tex&amp;gt;, таким образом, препроцессинг работает за &amp;lt;tex&amp;gt;O(n)&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;
*[[Решение RMQ с помощью разреженной таблицы]]&lt;br /&gt;
*[[Алгоритм Фарака-Колтона и Бендера]]&lt;br /&gt;
*[[Сведение задачи RMQ к задаче LCA]]&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
*[http://e-maxx.ru/algo/lca Наименьший общий предок. Нахождение за O (sqrt (N)) и O (log N) с препроцессингом O (N)]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Задача о наименьшем общем предке]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8_RMQ_%D0%BA_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B5_LCA&amp;diff=48141</id>
		<title>Сведение задачи RMQ к задаче LCA</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8_RMQ_%D0%BA_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B5_LCA&amp;diff=48141"/>
				<updated>2015-06-08T15:52:41Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Описание */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Шаблон:Задача&lt;br /&gt;
|definition =Дан массив &amp;lt;tex&amp;gt;A[1..N]&amp;lt;/tex&amp;gt;. Поступают запросы вида &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, на каждый запрос требуется найти минимум в массиве &amp;lt;tex&amp;gt;A&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;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Алгоритм ==&lt;br /&gt;
===Описание===&lt;br /&gt;
[[Файл:Wiki.PNG|thumb|right|300x150px|Пример декартового дерева]]&lt;br /&gt;
Будем решать задачу RMQ, уже умея решать задачу LCA. Для хранения решения задачи о LCA будем использовать  [[декартово дерево по неявному ключу]]. Тогда минимум на отрезке от &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;i&amp;lt;/tex&amp;gt;-того и &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-того элементов из декартова дерева. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Декартово дерево по неявному ключу на массиве &amp;lt;tex&amp;gt;A[1..N]&amp;lt;/tex&amp;gt; {{---}} это бинарное дерево, допускающее следующее рекурсивное построение:&lt;br /&gt;
* Корнем дерева является элемент массива, имеющий минимальное значение &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;, скажем &amp;lt;tex&amp;gt;A[i]&amp;lt;/tex&amp;gt;. Если минимальных элементов несколько, можно взять любой.&lt;br /&gt;
* Левым поддеревом является декартово дерево на массиве &amp;lt;tex&amp;gt;A[1..i-1]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
* Правым поддеревом является декартово дерево на массиве &amp;lt;tex&amp;gt;A[i+1..N]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Здесь и далее &amp;lt;tex&amp;gt;A[i]&amp;lt;/tex&amp;gt; будет также использоваться для обозначения соответствующей вершины дерева.&lt;br /&gt;
&lt;br /&gt;
Построим декартово дерево на массиве &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;. Тогда &amp;lt;tex&amp;gt;RMQ(i, j)&amp;lt;/tex&amp;gt; = &amp;lt;tex&amp;gt;LCA(A[i], A[j])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&amp;lt;br clear=&amp;quot;all&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Корректность ===&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement=&lt;br /&gt;
&amp;lt;tex&amp;gt;RMQ(i, j)&amp;lt;/tex&amp;gt; = &amp;lt;tex&amp;gt;LCA(A[i], A[j])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
Положим &amp;lt;tex&amp;gt;w = LCA(A[i], A[j])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что &amp;lt;tex&amp;gt;A[i]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;A[j]&amp;lt;/tex&amp;gt; не принадлежат одновременно либо правому, либо левому поддереву &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;, потому как тогда бы соответствующий сын находился на большей глубине, чем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;, и также являлся предком как &amp;lt;tex&amp;gt;A[i]&amp;lt;/tex&amp;gt; так и &amp;lt;tex&amp;gt;A[j]&amp;lt;/tex&amp;gt;, что противоречит определению &amp;lt;tex&amp;gt;LCA&amp;lt;/tex&amp;gt;. Из этого замечанию следует, что &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt; лежит между &amp;lt;tex&amp;gt;A[i]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;A[j]&amp;lt;/tex&amp;gt; и, следовательно, принадлежит отрезку &amp;lt;tex&amp;gt;A[i..j]&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;w&amp;lt;/tex&amp;gt; содержит в себе подмассив &amp;lt;tex&amp;gt;A[i..j]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Суммируя, получаем, что &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt; имеет минимальное значение на отрезке, покрывающем &amp;lt;tex&amp;gt;A[i..j]&amp;lt;/tex&amp;gt;, и принадлежит отрезку &amp;lt;tex&amp;gt;A[i..j]&amp;lt;/tex&amp;gt;, отсюда &amp;lt;tex&amp;gt;RMQ(i, j) = w&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;. Используя [[Сведение задачи LCA к задаче RMQ | алгоритм построения LCA]], получаем:&lt;br /&gt;
препроцессинг для &amp;lt;tex&amp;gt;LCA&amp;lt;/tex&amp;gt; {{---}} &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; и ответ на запрос {{---}} &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&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(1)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В итоге получили &amp;lt;tex&amp;gt;RMQ&amp;lt;/tex&amp;gt; с построением за &amp;lt;tex&amp;gt;O(n)&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;
*[[Сведение задачи LCA к задаче RMQ]]&lt;br /&gt;
*[[Декартово дерево]]&lt;br /&gt;
*[[Алгоритм Фарака-Колтона и Бендера]]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Задача о наименьшем общем предке]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8_RMQ_%D0%BA_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B5_LCA&amp;diff=48140</id>
		<title>Сведение задачи RMQ к задаче LCA</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8_RMQ_%D0%BA_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B5_LCA&amp;diff=48140"/>
				<updated>2015-06-08T15:46:45Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Алгоритм */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Шаблон:Задача&lt;br /&gt;
|definition =Дан массив &amp;lt;tex&amp;gt;A[1..N]&amp;lt;/tex&amp;gt;. Поступают запросы вида &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, на каждый запрос требуется найти минимум в массиве &amp;lt;tex&amp;gt;A&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;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Алгоритм ==&lt;br /&gt;
===Описание===&lt;br /&gt;
[[Файл:Wiki.PNG|thumb|right|300x150px|Пример декартового дерева]]&lt;br /&gt;
Будем решать задачу RMQ, уже умея решать задачу LCA, для хранения решения задачи о LCA будем использовать  [[декартово дерево по неявному ключу]]. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Декартово дерево по неявному ключу на массиве &amp;lt;tex&amp;gt;A[1..N]&amp;lt;/tex&amp;gt; {{---}} это бинарное дерево, допускающее следующее рекурсивное построение:&lt;br /&gt;
* Корнем дерева является элемент массива, имеющий минимальное значение &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;, скажем &amp;lt;tex&amp;gt;A[i]&amp;lt;/tex&amp;gt;. Если минимальных элементов несколько, можно взять любой.&lt;br /&gt;
* Левым поддеревом является декартово дерево на массиве &amp;lt;tex&amp;gt;A[1..i-1]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
* Правым поддеревом является декартово дерево на массиве &amp;lt;tex&amp;gt;A[i+1..N]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Здесь и далее &amp;lt;tex&amp;gt;A[i]&amp;lt;/tex&amp;gt; будет также использоваться для обозначения соответствующей вершины дерева.&lt;br /&gt;
&lt;br /&gt;
Построим декартово дерево на массиве &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;. Тогда &amp;lt;tex&amp;gt;RMQ(i, j)&amp;lt;/tex&amp;gt; = &amp;lt;tex&amp;gt;LCA(A[i], A[j])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&amp;lt;br clear=&amp;quot;all&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Корректность ===&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement=&lt;br /&gt;
&amp;lt;tex&amp;gt;RMQ(i, j)&amp;lt;/tex&amp;gt; = &amp;lt;tex&amp;gt;LCA(A[i], A[j])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
Положим &amp;lt;tex&amp;gt;w = LCA(A[i], A[j])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что &amp;lt;tex&amp;gt;A[i]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;A[j]&amp;lt;/tex&amp;gt; не принадлежат одновременно либо правому, либо левому поддереву &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;, потому как тогда бы соответствующий сын находился на большей глубине, чем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;, и также являлся предком как &amp;lt;tex&amp;gt;A[i]&amp;lt;/tex&amp;gt; так и &amp;lt;tex&amp;gt;A[j]&amp;lt;/tex&amp;gt;, что противоречит определению &amp;lt;tex&amp;gt;LCA&amp;lt;/tex&amp;gt;. Из этого замечанию следует, что &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt; лежит между &amp;lt;tex&amp;gt;A[i]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;A[j]&amp;lt;/tex&amp;gt; и, следовательно, принадлежит отрезку &amp;lt;tex&amp;gt;A[i..j]&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;w&amp;lt;/tex&amp;gt; содержит в себе подмассив &amp;lt;tex&amp;gt;A[i..j]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Суммируя, получаем, что &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt; имеет минимальное значение на отрезке, покрывающем &amp;lt;tex&amp;gt;A[i..j]&amp;lt;/tex&amp;gt;, и принадлежит отрезку &amp;lt;tex&amp;gt;A[i..j]&amp;lt;/tex&amp;gt;, отсюда &amp;lt;tex&amp;gt;RMQ(i, j) = w&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;. Используя [[Сведение задачи LCA к задаче RMQ | алгоритм построения LCA]], получаем:&lt;br /&gt;
препроцессинг для &amp;lt;tex&amp;gt;LCA&amp;lt;/tex&amp;gt; {{---}} &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; и ответ на запрос {{---}} &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&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(1)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В итоге получили &amp;lt;tex&amp;gt;RMQ&amp;lt;/tex&amp;gt; с построением за &amp;lt;tex&amp;gt;O(n)&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;
*[[Сведение задачи LCA к задаче RMQ]]&lt;br /&gt;
*[[Декартово дерево]]&lt;br /&gt;
*[[Алгоритм Фарака-Колтона и Бендера]]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Задача о наименьшем общем предке]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8_LCA_%D0%BA_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B5_RMQ&amp;diff=48139</id>
		<title>Сведение задачи LCA к задаче RMQ</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8_LCA_%D0%BA_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B5_RMQ&amp;diff=48139"/>
				<updated>2015-06-08T15:22:52Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Запрос */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Шаблон:Задача&lt;br /&gt;
|definition = &lt;br /&gt;
Пусть дано корневое дерево &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. На вход подаются запросы вида &amp;lt;tex&amp;gt;(u,\;v)&amp;lt;/tex&amp;gt;, для каждого запроса требуется найти их наименьшего общего предка.&lt;br /&gt;
}}&lt;br /&gt;
{{Определение &lt;br /&gt;
|id=lca_suf_tree&lt;br /&gt;
|definition = '''Наименьшим общим предком''' (англ. ''least common ancestor'') двух узлов &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;T&amp;lt;/tex&amp;gt; называется узел &amp;lt;tex&amp;gt;w&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;, имеет наибольшую глубину.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Алгоритм ==&lt;br /&gt;
=== Препроцессинг ===&lt;br /&gt;
Для каждой вершины &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; определим глубину с помощью следующей рекурсивной формулы:&lt;br /&gt;
:&amp;lt;tex&amp;gt;\mathrm{depth}(u) = \begin{cases}&lt;br /&gt;
0 &amp;amp; u = \mathrm{root}(T),\\&lt;br /&gt;
\mathrm{depth}(v) + 1 &amp;amp; u = \mathrm{son}(v).&lt;br /&gt;
\end{cases}&amp;lt;/tex&amp;gt;&lt;br /&gt;
Ясно, что глубина вершины элементарным образом поддерживается во время обхода в глубину.&lt;br /&gt;
&lt;br /&gt;
Запустим [[Обход в глубину, цвета вершин|обход в глубину]] из корня, который будет вычислять значения следующих величин: &lt;br /&gt;
#Cписок глубин посещенных вершин &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;. Глубина текущей вершины добавляется в конец списка при входе в данную вершину, а также после каждого возвращения из её сына.&lt;br /&gt;
#Список посещений узлов &amp;lt;tex&amp;gt;\mathtt{vtx}&amp;lt;/tex&amp;gt;, строящийся аналогично предыдущему, только добавляется не глубина а сама вершина.&lt;br /&gt;
#Значение функции &amp;lt;tex&amp;gt;\mathtt{I}[u]&amp;lt;/tex&amp;gt;, возвращающей любой индекс в списке глубин &amp;lt;tex&amp;gt;d&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;
Будем считать, что &amp;lt;tex&amp;gt;\mathrm{rmq}(d,l,r)&amp;lt;/tex&amp;gt; возвращает индекс минимального элемента в &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; на отрезке &amp;lt;tex&amp;gt;[l..r]&amp;lt;/tex&amp;gt;. Тогда ответом на запрос &amp;lt;tex&amp;gt;\mathrm{lca}(u, v)&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;\mathtt{I}[u] \leqslant \mathtt{I}[v]&amp;lt;/tex&amp;gt;, будет &amp;lt;tex&amp;gt;\mathtt{vtx}[\mathrm{rmq}(d,\mathtt{I}[u], \mathtt{I}[v])]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Доказательство корректности алгоритма ===&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement=&lt;br /&gt;
Наименьшему общему предку вершин &amp;lt;tex&amp;gt;u, v&amp;lt;/tex&amp;gt; соответствует минимальная глубина на отрезке &amp;lt;tex&amp;gt;d[\mathtt{I}[u], \mathtt{I}[v]]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим два узла &amp;lt;tex&amp;gt;u, v&amp;lt;/tex&amp;gt; корневого дерева &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. Рассмотрим отрезок &amp;lt;tex&amp;gt;d[\mathtt{I}[u]..\mathtt{I}[v]]&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;w&amp;lt;/tex&amp;gt; (в дереве есть только один простой путь между вершинами), а следовательно минимум на отрезке никак не больше глубины &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Заметим, что в момент добавления &amp;lt;tex&amp;gt;\mathtt{I}[u]&amp;lt;/tex&amp;gt; обход посещал поддерево с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. В момент добавления &amp;lt;tex&amp;gt;\mathtt{I}[v]&amp;lt;/tex&amp;gt; мы все еще в поддереве с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Значит, и на отрезке между &amp;lt;tex&amp;gt;\mathtt{I}[u]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathtt{I}[v]&amp;lt;/tex&amp;gt; мы находились внутри поддерева с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Отсюда сделаем заключение, что на рассматриваемом отрезке не посещалась вершина, отличная от &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;, с глубиной меньшей либо равной глубины &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;, т. к. подобной вершины нет в поддереве с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}.&lt;br /&gt;
&lt;br /&gt;
== Пример ==&lt;br /&gt;
Рассмотрим дерево на рисунке 1. Найдем наименьшего общего предка вершин, помеченных красным цветом.&lt;br /&gt;
Список глубин, получающийся в результате обхода в глубину {{---}} &amp;lt;tex&amp;gt;[0, 1, 2, 1, 2, 1, 0, 1, 0].&amp;lt;/tex&amp;gt;&lt;br /&gt;
Глубина наименьшего общего предка красных вершин равна минимуму на отрезке &amp;lt;tex&amp;gt;[2, 1, 0, 1].&amp;lt;/tex&amp;gt;&lt;br /&gt;
[[Файл:Lca to rmq.png(2).png|thumb|left|Рисунок к примеру]]&lt;br /&gt;
&amp;lt;div style='clear:left;'&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Сложность ==&lt;br /&gt;
&lt;br /&gt;
Для нахождения минимального элемента на отрезке можно использовать [[Алгоритм Фарака-Колтона и Бендера|алгоритм Фарака-Колтона и Бендера]] для &amp;lt;tex&amp;gt;\pm 1RMQ&amp;lt;/tex&amp;gt;, т. к. соседние элементы в списке глубин отличаются не более чем на единицу. Длина списка глубин составляет &amp;lt;tex&amp;gt;(2n - 1)&amp;lt;/tex&amp;gt;, таким образом, препроцессинг работает за &amp;lt;tex&amp;gt;O(n)&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;
*[[Решение RMQ с помощью разреженной таблицы]]&lt;br /&gt;
*[[Алгоритм Фарака-Колтона и Бендера]]&lt;br /&gt;
*[[Сведение задачи RMQ к задаче LCA]]&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
*[http://e-maxx.ru/algo/lca Наименьший общий предок. Нахождение за O (sqrt (N)) и O (log N) с препроцессингом O (N)]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Задача о наименьшем общем предке]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8_LCA_%D0%BA_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B5_RMQ&amp;diff=47937</id>
		<title>Сведение задачи LCA к задаче RMQ</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8_LCA_%D0%BA_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B5_RMQ&amp;diff=47937"/>
				<updated>2015-06-07T06:43:28Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Препроцессинг */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Шаблон:Задача&lt;br /&gt;
|definition = &lt;br /&gt;
Пусть дано корневое дерево &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. На вход подаются запросы вида &amp;lt;tex&amp;gt;(u,\;v)&amp;lt;/tex&amp;gt;, для каждого запроса требуется найти их наименьшего общего предка.&lt;br /&gt;
}}&lt;br /&gt;
{{Определение &lt;br /&gt;
|id=lca_suf_tree&lt;br /&gt;
|definition = '''Наименьшим общим предком''' (англ. ''least common ancestor'') двух узлов &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;T&amp;lt;/tex&amp;gt; называется узел &amp;lt;tex&amp;gt;w&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;, имеет наибольшую глубину.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Алгоритм ==&lt;br /&gt;
=== Препроцессинг ===&lt;br /&gt;
Для каждой вершины &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; определим глубину с помощью следующей рекурсивной формулы:&lt;br /&gt;
:&amp;lt;tex&amp;gt;\mathrm{depth}(u) = \begin{cases}&lt;br /&gt;
0 &amp;amp; u = \mathrm{root}(T),\\&lt;br /&gt;
\mathrm{depth}(v) + 1 &amp;amp; u = \mathrm{son}(v).&lt;br /&gt;
\end{cases}&amp;lt;/tex&amp;gt;&lt;br /&gt;
Ясно, что глубина вершины элементарным образом поддерживается во время обхода в глубину.&lt;br /&gt;
&lt;br /&gt;
Запустим [[Обход в глубину, цвета вершин|обход в глубину]] из корня, который будет вычислять значения следующих величин: &lt;br /&gt;
#Cписок глубин посещенных вершин &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;. Глубина текущей вершины добавляется в конец списка при входе в данную вершину, а также после каждого возвращения из её сына.&lt;br /&gt;
#Список посещений узлов &amp;lt;tex&amp;gt;\mathtt{vtx}&amp;lt;/tex&amp;gt;, строящийся аналогично предыдущему, только добавляется не глубина а сама вершина.&lt;br /&gt;
#Значение функции &amp;lt;tex&amp;gt;\mathtt{I}[u]&amp;lt;/tex&amp;gt;, возвращающей любой индекс в списке глубин &amp;lt;tex&amp;gt;d&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;
Будем считать, что &amp;lt;tex&amp;gt;\mathrm{rmq}(d,l,r)&amp;lt;/tex&amp;gt; возвращает индекс минимального элемента в &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; на отрезке &amp;lt;tex&amp;gt;[l..r]&amp;lt;/tex&amp;gt;. Тогда ответом на запрос &amp;lt;tex&amp;gt;\mathrm{lca}(u, v)&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;I[u] \leqslant \mathtt{I}[v]&amp;lt;/tex&amp;gt;, будет &amp;lt;tex&amp;gt;\mathtt{vtx}[\mathrm{rmq}(d,\mathtt{I}[u], \mathtt{I}[v])]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Доказательство корректности алгоритма ===&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement=&lt;br /&gt;
Наименьшему общему предку вершин &amp;lt;tex&amp;gt;u, v&amp;lt;/tex&amp;gt; соответствует минимальная глубина на отрезке &amp;lt;tex&amp;gt;d[\mathtt{I}[u], \mathtt{I}[v]]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим два узла &amp;lt;tex&amp;gt;u, v&amp;lt;/tex&amp;gt; корневого дерева &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. Рассмотрим отрезок &amp;lt;tex&amp;gt;d[\mathtt{I}[u]..\mathtt{I}[v]]&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;w&amp;lt;/tex&amp;gt; (в дереве есть только один простой путь между вершинами), а следовательно минимум на отрезке никак не больше глубины &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Заметим, что в момент добавления &amp;lt;tex&amp;gt;\mathtt{I}[u]&amp;lt;/tex&amp;gt; обход посещал поддерево с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. В момент добавления &amp;lt;tex&amp;gt;\mathtt{I}[v]&amp;lt;/tex&amp;gt; мы все еще в поддереве с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Значит, и на отрезке между &amp;lt;tex&amp;gt;\mathtt{I}[u]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathtt{I}[v]&amp;lt;/tex&amp;gt; мы находились внутри поддерева с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Отсюда сделаем заключение, что на рассматриваемом отрезке не посещалась вершина, отличная от &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;, с глубиной меньшей либо равной глубины &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;, т. к. подобной вершины нет в поддереве с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}.&lt;br /&gt;
&lt;br /&gt;
== Пример ==&lt;br /&gt;
Рассмотрим дерево на рисунке 1. Найдем наименьшего общего предка вершин, помеченных красным цветом.&lt;br /&gt;
Список глубин, получающийся в результате обхода в глубину {{---}} &amp;lt;tex&amp;gt;[0, 1, 2, 1, 2, 1, 0, 1, 0].&amp;lt;/tex&amp;gt;&lt;br /&gt;
Глубина наименьшего общего предка красных вершин равна минимуму на отрезке &amp;lt;tex&amp;gt;[2, 1, 0, 1].&amp;lt;/tex&amp;gt;&lt;br /&gt;
[[Файл:Lca to rmq.png(2).png|thumb|left|Рисунок к примеру]]&lt;br /&gt;
&amp;lt;div style='clear:left;'&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Сложность ==&lt;br /&gt;
&lt;br /&gt;
Для нахождения минимального элемента на отрезке можно использовать [[Алгоритм Фарака-Колтона и Бендера|алгоритм Фарака-Колтона и Бендера]] для &amp;lt;tex&amp;gt;\pm 1RMQ&amp;lt;/tex&amp;gt;, т. к. соседние элементы в списке глубин отличаются не более чем на единицу. Длина списка глубин составляет &amp;lt;tex&amp;gt;(2n - 1)&amp;lt;/tex&amp;gt;, таким образом, препроцессинг работает за &amp;lt;tex&amp;gt;O(n)&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;
*[[Решение RMQ с помощью разреженной таблицы]]&lt;br /&gt;
*[[Алгоритм Фарака-Колтона и Бендера]]&lt;br /&gt;
*[[Сведение задачи RMQ к задаче LCA]]&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
*[http://e-maxx.ru/algo/lca Наименьший общий предок. Нахождение за O (sqrt (N)) и O (log N) с препроцессингом O (N)]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Задача о наименьшем общем предке]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8_RMQ_%D0%BA_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B5_LCA&amp;diff=47936</id>
		<title>Сведение задачи RMQ к задаче LCA</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8_RMQ_%D0%BA_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B5_LCA&amp;diff=47936"/>
				<updated>2015-06-07T06:41:44Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Шаблон:Задача&lt;br /&gt;
|definition =Дан массив &amp;lt;tex&amp;gt;A[1..N]&amp;lt;/tex&amp;gt;. Поступают запросы вида &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, на каждый запрос требуется найти минимум в массиве &amp;lt;tex&amp;gt;A&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;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Алгоритм ==&lt;br /&gt;
===Описание===&lt;br /&gt;
[[Файл:Wiki.PNG|thumb|right|300x150px|Пример декартового дерева]]&lt;br /&gt;
Для нахождения минимума на отрезке будем использовать информацию о наименьшем общем предке. Для хранения наименьших общих предков будем использовать  [[декартово дерево по неявному ключу]].&lt;br /&gt;
&lt;br /&gt;
Декартово дерево по неявному ключу на массиве &amp;lt;tex&amp;gt;A[1..N]&amp;lt;/tex&amp;gt; {{---}} это бинарное дерево, допускающее следующее рекурсивное построение:&lt;br /&gt;
* Корнем дерева является элемент массива, имеющий минимальное значение &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;, скажем &amp;lt;tex&amp;gt;A[i]&amp;lt;/tex&amp;gt;. Если минимальных элементов несколько, можно взять любой.&lt;br /&gt;
* Левым поддеревом является декартово дерево на массиве &amp;lt;tex&amp;gt;A[1..i-1]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
* Правым поддеревом является декартово дерево на массиве &amp;lt;tex&amp;gt;A[i+1..N]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Здесь и далее &amp;lt;tex&amp;gt;A[i]&amp;lt;/tex&amp;gt; будет также использоваться для обозначения соответствующей вершины дерева.&lt;br /&gt;
&lt;br /&gt;
Построим декартово дерево на массиве &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;. Тогда &amp;lt;tex&amp;gt;RMQ(i, j)&amp;lt;/tex&amp;gt; = &amp;lt;tex&amp;gt;LCA(A[i], A[j])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&amp;lt;br clear=&amp;quot;all&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Корректность ===&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement=&lt;br /&gt;
&amp;lt;tex&amp;gt;RMQ(i, j)&amp;lt;/tex&amp;gt; = &amp;lt;tex&amp;gt;LCA(A[i], A[j])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
Положим &amp;lt;tex&amp;gt;w = LCA(A[i], A[j])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что &amp;lt;tex&amp;gt;A[i]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;A[j]&amp;lt;/tex&amp;gt; не принадлежат одновременно либо правому, либо левому поддереву &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;, потому как тогда бы соответствующий сын находился на большей глубине, чем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;, и также являлся предком как &amp;lt;tex&amp;gt;A[i]&amp;lt;/tex&amp;gt; так и &amp;lt;tex&amp;gt;A[j]&amp;lt;/tex&amp;gt;, что противоречит определению &amp;lt;tex&amp;gt;LCA&amp;lt;/tex&amp;gt;. Из этого замечанию следует, что &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt; лежит между &amp;lt;tex&amp;gt;A[i]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;A[j]&amp;lt;/tex&amp;gt; и, следовательно, принадлежит отрезку &amp;lt;tex&amp;gt;A[i..j]&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;w&amp;lt;/tex&amp;gt; содержит в себе подмассив &amp;lt;tex&amp;gt;A[i..j]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Суммируя, получаем, что &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt; имеет минимальное значение на отрезке, покрывающем &amp;lt;tex&amp;gt;A[i..j]&amp;lt;/tex&amp;gt;, и принадлежит отрезку &amp;lt;tex&amp;gt;A[i..j]&amp;lt;/tex&amp;gt;, отсюда &amp;lt;tex&amp;gt;RMQ(i, j) = w&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;. Используя [[Сведение задачи LCA к задаче RMQ | алгоритм построения LCA]], получаем:&lt;br /&gt;
препроцессинг для &amp;lt;tex&amp;gt;LCA&amp;lt;/tex&amp;gt; {{---}} &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; и ответ на запрос {{---}} &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&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(1)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В итоге получили &amp;lt;tex&amp;gt;RMQ&amp;lt;/tex&amp;gt; с построением за &amp;lt;tex&amp;gt;O(n)&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;
*[[Сведение задачи LCA к задаче RMQ]]&lt;br /&gt;
*[[Декартово дерево]]&lt;br /&gt;
*[[Алгоритм Фарака-Колтона и Бендера]]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Задача о наименьшем общем предке]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=47935</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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=47935"/>
				<updated>2015-06-07T06:39:25Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Пример */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Алгоритм Карккайнена-Сандерса''' (Kärkkäinen, Sanders) — алгоритм построения [[суффиксный массив | суффиксного массива]] за линейное время.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Используемые обозначения ==&lt;br /&gt;
* В данном конспекте используется 0-индексация.&lt;br /&gt;
* &amp;lt;tex&amp;gt;S[i..j] &amp;lt;/tex&amp;gt; — подстрока строки &amp;lt;tex&amp;gt; S &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;-й символы включительно.&lt;br /&gt;
* Пусть длина строки &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;. Обозначим &amp;lt;tex&amp;gt; S[i..j] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; j \geqslant n &amp;lt;/tex&amp;gt; как строку &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt;, дополненную защитными символами &amp;lt;tex&amp;gt; \$ &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;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Четным суффиксом''' назовем суффикс, начинающийся в четной позиции. &amp;lt;br&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;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left( \dfrac{n}{2}\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого является &amp;lt;tex&amp;gt; T(n) = O(n) &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;
&lt;br /&gt;
* '''Суффиксный массив для нечетных суффиксов'''&lt;br /&gt;
&lt;br /&gt;
# Отобразим исходную строку &amp;lt;tex&amp;gt; S &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; длины  &amp;lt;tex&amp;gt; \dfrac{n}{2} &amp;lt;/tex&amp;gt;  следующим образом:&lt;br /&gt;
#* Сделаем список, состоящий из пар символов вида &amp;lt;tex&amp;gt; S[i..i + 1] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; i \bmod 2 = 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его цифровой сортировкой за линейное время и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S[1..n] &amp;lt;/tex&amp;gt; в алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;, получив строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; половинной длины.&lt;br /&gt;
# Рекурсивно построим суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Построим суффиксный массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;. Очевидно, &amp;lt;tex&amp;gt; A_{S_o}[i] = 2 A_{S'}[i] + 1 &amp;lt;/tex&amp;gt;, так отношение упорядоченности любых двух строк в старом алфавите &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt; эквивалентно отношению упорядоченности в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; по его построению.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для четных суффиксов'''&lt;br /&gt;
На этом шаге мы за линейное время получим суффиксный массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; для четных суффиксов, используя уже построенный &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что сортировка множества четных суффиксов &amp;lt;tex&amp;gt; \{ S[i..n] | i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке множества пар &amp;lt;tex&amp;gt; \{ (S[i], S[i+1..n]) | i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt;. Однако &amp;lt;tex&amp;gt; S[i+1..n] &amp;lt;/tex&amp;gt; — нечетный суффикс, и его относительную позицию мы уже узнали на шаге 1.&lt;br /&gt;
&lt;br /&gt;
Таким образом, чтобы отсортировать эти пары за линейное время, сначала сразу выпишем их в порядке возрастания второго элемента пары (то есть в порядке вхождения в массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;), а потом отсортируем устойчивой сортировкой подсчетом по первым элементам. После этого легко можно восстановить массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
     M.add(Pair(S[&amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
    &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&gt;
&lt;br /&gt;
Заметим, что массив &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; перед сортировкой подсчетом не был явно отсортирован по вторым элементам, и хранил не суффиксы, а их позиции в строке &amp;lt;tex&amp;gt; S &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;
Для суффиксного дерева третий шаг алгоритма опирается на специфические особенности суффиксных деревьев, которые не присущи суффиксным массивам&amp;lt;ref&amp;gt;M. Farach. Optimal suffix tree construction with large alphabets. http://www.cs.rutgers.edu/~farach/pubs/FarFerrMuthu00.pdf &amp;lt;/ref&amp;gt; .  &lt;br /&gt;
В случае суффиксного массива слияние становится очень сложным, но все же оно было реализовано в алгоритме Ким-Сим-Парк-Парка&amp;lt;ref&amp;gt; D. K. Kim, J. S. Sim, H. Park, and K. Park. Linear-time construction of suffix arrays. http://www.springerlink.com/content/568156021q45r320/&amp;lt;/ref&amp;gt;. Однако простой модификацией алгоритма можно значительно упростить его.&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
Покажем первые два шага агоритма для строки '''ababbbaa'''.&lt;br /&gt;
&lt;br /&gt;
Во-первых, добавив защитный символ '''$''', получив строку '''ababbbaa$''' (для этого алгоритма он не требуется, но может понадобиться в применениях суффиксного массива). Во-вторых, дополним ее до четной длины, получив '''ababbbaa$$'''.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для нечетных суффиксов'''&lt;br /&gt;
# В новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет четыре элемента — '''ba''', '''bb''', '''a$''', '''$$'''. После сортировки они получат номера 2, 3, 1 и 0 соответственно.&lt;br /&gt;
# Переводим строку S[1..n] = '''babbbaa$$$''' в новый алфавит. Сжатой строкой &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; будет '''23210'''.&lt;br /&gt;
# После рекурсивного вызова получим, что &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [4, 3, 2, 0, 1], и &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; = [9, 7, 5, 1, 3].&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для четных суффиксов'''&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''$''', 9), ('''a''', 7), ('''b''', 5), ('''a''', 1), ('''a''', 3)].&lt;br /&gt;
# После сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt;= [('''$''', 9), ('''a''', 7), ('''a''', 1), ('''a''', 3), ('''b''', 5)].&lt;br /&gt;
# Восстановив массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;, получаем [8, 6, 0, 2, 4], что действительно является суффиксным массивом для четных суффиксов.&lt;br /&gt;
&lt;br /&gt;
* '''Слияние суффиксных массивов'''&lt;br /&gt;
Если бы мы умели сливать &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; за линейное время, получили бы:&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 9 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; a\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; aa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ababbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; baa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; babbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Как и было сказано вначале, избавиться от лишних '''$''' легко, так как суффиксы, им соответствующие, будут первыми в суффиксном массиве (в данном случае достаточно выбросить &amp;quot;9&amp;quot; из суффиксного массива).&lt;br /&gt;
&lt;br /&gt;
== Алгоритм Карккайнена-Сандерса == &lt;br /&gt;
Изменим изначальный алгоритм следующим образом:&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих позициям, не кратным трем. Рекурсивно сведем это к построению суффиксного массива для строки длиной в две трети исходной.&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих кратным трем позициям, используя результат первого шага, за линейное время.&lt;br /&gt;
# Сольем эти суффиксные массивы в один за линейное время.&lt;br /&gt;
&lt;br /&gt;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left(\dfrac23 n\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого также является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt; (это видно из того, что сумма геометрической прогрессии с основанием &amp;lt;tex&amp;gt; \dfrac23 &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Аналогично первой версии алгоритма, дополним строку &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; до длины, кратной трем, защитными символами &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* '''База рекурсии'''&lt;br /&gt;
Для этого алгоритма минимальной базой рекурсии будет строка длиной 4, так как она дополняется до длины 6, после чего вновь следует рекурсивный вызов строки длиной 4, и, если бы база была меньше 4, алгоритм вошел бы в бесконечную рекурсию. На этом этапе также можно применить обычную сортировку суффиксов, так как это потребует &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
На этом шаге строится суффиксный массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; для множества суффиксов &amp;lt;tex&amp;gt; \{ S[i..n-1] | i \bmod 3 \ne 0 \} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Получим строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; аналогично предыдущему алгоритму:&lt;br /&gt;
#* Сделаем список, состоящий из троек &amp;lt;tex&amp;gt; S[i..i+2]&amp;lt;/tex&amp;gt; , где &amp;lt;tex&amp;gt; i \bmod 3 \ne 0 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его за линейное время цифровой сортировкой и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S[1..n]S[2..n+1] &amp;lt;/tex&amp;gt; в строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; длиной &amp;lt;tex&amp;gt; \dfrac23 n &amp;lt;/tex&amp;gt; в алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;. Тогда суффиксу &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt; в старом алфавите, где &amp;lt;tex&amp;gt; i \bmod 3 = 1 &amp;lt;/tex&amp;gt;, в новом алфавите будет соответствовать строка &amp;lt;tex&amp;gt; S'\left[\dfrac{i-1}{3}..\dfrac{n}{3} - 1\right] &amp;lt;/tex&amp;gt;, а если &amp;lt;tex&amp;gt; i\bmod 3 = 2 &amp;lt;/tex&amp;gt;, то строка &amp;lt;tex&amp;gt; S'\left[\dfrac{n}{3} + \dfrac{i-2}{3}..\dfrac{2n}{3} - 1\right] &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Вызовем алгоритм рекурсивно для строки &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt;, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Пройдем по массиву &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt; A_{S'}[i] &amp;lt; \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j = 3A_{S'}[i] + 1 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;, если же &amp;lt;tex&amp;gt; A_{S'}[i] \geqslant \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j =  3\left(A_{S'}[i] - \dfrac{n}{3}\right) + 2 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;. Псевдокод получения &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;.length - 1:&lt;br /&gt;
    '''if''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] &amp;lt; n/3:&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] + 1)&lt;br /&gt;
    '''else''':&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * (&amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] - n/3) + 2)&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
Этот шаг также аналогичен первой версии алгоритма. Сортировка множества &amp;lt;tex&amp;gt; \{ S[i..n-1] | i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке пар &amp;lt;tex&amp;gt; \{ (S[i], S[i+1..n-1]) | i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; S[i+1..n-1] &amp;lt;/tex&amp;gt; — суффиксы в позициях, равных 1 по модулю 3, относительный порядок которых уже известен. Выпишем эти пары в порядке вхождения их в &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и отсортируем по первому элементу устойчивой сортировкой подсчетом, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;. Псевдокод этого шага:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' 2n/3 - 1:&lt;br /&gt;
     '''if''' &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[i] % 3 == 1:&lt;br /&gt;
         M.add(Pair(S[&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 '''for''' i = 0 '''to''' n/3 - 1:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&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; A_{S_0} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, чтобы получить суффиксный массив &amp;lt;tex&amp;gt; A_{S} &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;
&lt;br /&gt;
Пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 1 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Это аналогично сравнению пар &amp;lt;tex&amp;gt; (S[i], S[i+1..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S[j], S[j+1..n-1]) &amp;lt;/tex&amp;gt;. Сравнить первые элементы пар мы можем за &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt;, а относительный порядок вторых элементов пар нам уже известен, так как они соответствуют позициям, равным 2 и 1 по модулю 3 соответственно.&lt;br /&gt;
&lt;br /&gt;
Аналогично, пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 2 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Тогда это аналогично сравнению троек &amp;lt;tex&amp;gt; (S[i], S[i+1], S[i+2..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S[j], S[j+1], S[j+2..n-1]) &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;A_{S}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 // Вначале предподсчитаем за O(n) обратную перестановку для суффиксного массива &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;, то есть массив rank такой, что &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[rank[i]] = i.&lt;br /&gt;
 // Тогда мы сможем за O(1) сравнивать суффиксы по их позиции.&lt;br /&gt;
 rank = inverse(&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;) &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3 '''and''' j &amp;lt; n/3:&lt;br /&gt;
     pos12 = &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i]&lt;br /&gt;
     pos0  = &amp;lt;tex&amp;gt; A_{0} &amp;lt;/tex&amp;gt;[j]&lt;br /&gt;
     '''if''' pos12 % 3 == 1:&lt;br /&gt;
        ''' if''' Pair(S[pos12], rank[pos12 + 1]) &amp;lt; Pair(S[pos0], rank[pos0 + 1]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++  &lt;br /&gt;
     '''else''':&lt;br /&gt;
         '''if''' Triple(S[pos12], S[pos12 + 1], rank[pos12 + 2]) &amp;lt; Triple(S[pos0], S[pos0 + 1], rank[pos0 + 2]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++ &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i])&lt;br /&gt;
     i++&lt;br /&gt;
 '''while''' j &amp;lt; n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{0}} &amp;lt;/tex&amp;gt;[j])&lt;br /&gt;
     j++&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;
Построим суффиксный массив для строки '''abbacab'''. После добавления защитного символа и дополнения до кратной трем длины, получим '''abbacab$$'''.&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
# Тройками, соответствующими равными 1 по модулю 3 позициям, будут: '''bba''', '''cab''', '''$$$''', соответствующими равным 2 по модулю 3 — '''bac''', '''cab$''', '''$$$'''. Новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет содержать элементы '''bba''', '''cab''', '''$$$''', '''bac''', '''ab$''', которые после сортировки получат номера 3, 4, 0, 2, 1 соответственно.&lt;br /&gt;
# Строкой '''bbacab$$$bacab$$$$''' в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; = 340210. &lt;br /&gt;
# После рекурсивного вызова получим &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [5, 2, 4, 3, 0, 1]. Пересчитав &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, получим [(5 - 3)*3 + 2, 2 * 3 + 1, (4 - 3) * 3 + 2, (3 - 3) * 3 + 2, 0 * 3 + 1, 1 * 3 + 1] = [8, 7, 5, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и выбрав в нем элементы, равные 1 по модулю 3, получим массив пар &amp;lt;tex&amp;gt;M&amp;lt;/tex&amp;gt; = [(&amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, 7), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 1), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 4)]&lt;br /&gt;
# После устойчивой сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''a''', 1), ('''a''', 4), ('''b''', 7)]&lt;br /&gt;
# Восстановив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;, получаем [0, 3, 6].&lt;br /&gt;
* '''Слияние суффиксных массивов'''&lt;br /&gt;
Рассмотрим, к примеру, третью итерацию слияния, к этой итерации массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 2, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0, на ней мы сливаем суффиксы, соответствующие позициям 5 и 0. &lt;br /&gt;
# Образуем тройки (S[5], S[6], S[7..8]) и (S[0], S[1], S[2..8]).&lt;br /&gt;
# После получения относительного порядка суффиксов, получим тройки ('''a''', '''b''', 1) и ('''a''', '''b''', 3). Первая тройка меньше второй, поэтому добавляем суффикс, соответствующий позиции 5 в массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# В конце итерации получаем &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 3, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0.&lt;br /&gt;
К концу слияния получим &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5, 0, 3, 6, 2, 1, 4]. Так как мы добавляли один символ &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; в начале алгоритма для дополнения строки до длины, кратной трем, выбросим последний суффикс из &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;, получим в итоге, что &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [7, 5, 0, 3, 6, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; acab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; b\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; cab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
Заметим, что №9 будет выброшен, так как в начале алгоритма был добавлен один &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt; к строке&lt;br /&gt;
 &lt;br /&gt;
{|&lt;br /&gt;
| [[Файл:Kark_sanders_stage1.png|325px|thumb| Фаза 1]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage2.png|325px|thumb| Фаза 2]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage3.png|325px|thumb| Фаза 3]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Получение массива LCP ==&lt;br /&gt;
Массив LCP можно получить за линейное время [[Алгоритм_Касаи_и_др. | алгоритмом Касаи]].&lt;br /&gt;
&lt;br /&gt;
== Обобщение алгоритма ==&lt;br /&gt;
На самом деле, алгоритм можно обобщить&amp;lt;ref name=&amp;quot;generalisation&amp;quot;&amp;gt; Juha Kärkkäinen, Peter Sanders and Stefan Burkhardt. Linear work suffix array construction. http://www.cs.helsinki.fi/juha.karkkainen/publications/jacm05-revised.pdf &amp;lt;/ref&amp;gt;, взяв на первом шаге, к примеру, суффиксы, позиции которых по модулю 7 дают 3, 5 и 6. Для этого потребуются некоторое усложнение алгоритма, например, сортировка оставшихся суффиксов в нескольких группах на шаге 2 и слияние нескольких групп на шаге 3, но основная идея алгоритма остается той же. Множества, которые можно выбрать, на первом шаге определяются '''разностным покрытием''' (''difference cover'').&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Разностное покрытие''' (''difference cover'') &amp;lt;tex&amp;gt; D &amp;lt;/tex&amp;gt; по модулю &amp;lt;tex&amp;gt;m &amp;lt;/tex&amp;gt; — множество чисел от &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;m - 1 &amp;lt;/tex&amp;gt; таких, что &amp;lt;tex&amp;gt; \forall i \in [0, m-1]: \exists j, k \in D: i \equiv k - j \pmod m &amp;lt;/tex&amp;gt;.&lt;br /&gt;
}} &lt;br /&gt;
Например, &amp;lt;tex&amp;gt; \{1, 2\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt; \{3, 5, 6\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt; \{1\} &amp;lt;/tex&amp;gt; — не является разностным покрытием по модулю &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;, поэтому этот алгоритм не применим к нему. Подробнее узнать, как вычислять разностное покрытие для заданного модуля можно также здесь&amp;lt;ref name=&amp;quot;generalisation&amp;quot;/&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Источники информации==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
* [http://www.cs.helsinki.fi/juha.karkkainen/publications/icalp03.pdf Juha Kärkkäinen and Peter Sanders. Simple linear work suffix array construction]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Суффиксный массив]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=47934</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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=47934"/>
				<updated>2015-06-07T06:32:35Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Пример */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Алгоритм Карккайнена-Сандерса''' (Kärkkäinen, Sanders) — алгоритм построения [[суффиксный массив | суффиксного массива]] за линейное время.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Используемые обозначения ==&lt;br /&gt;
* В данном конспекте используется 0-индексация.&lt;br /&gt;
* &amp;lt;tex&amp;gt;S[i..j] &amp;lt;/tex&amp;gt; — подстрока строки &amp;lt;tex&amp;gt; S &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;-й символы включительно.&lt;br /&gt;
* Пусть длина строки &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;. Обозначим &amp;lt;tex&amp;gt; S[i..j] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; j \geqslant n &amp;lt;/tex&amp;gt; как строку &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt;, дополненную защитными символами &amp;lt;tex&amp;gt; \$ &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;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Четным суффиксом''' назовем суффикс, начинающийся в четной позиции. &amp;lt;br&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;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left( \dfrac{n}{2}\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого является &amp;lt;tex&amp;gt; T(n) = O(n) &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;
&lt;br /&gt;
* '''Суффиксный массив для нечетных суффиксов'''&lt;br /&gt;
&lt;br /&gt;
# Отобразим исходную строку &amp;lt;tex&amp;gt; S &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; длины  &amp;lt;tex&amp;gt; \dfrac{n}{2} &amp;lt;/tex&amp;gt;  следующим образом:&lt;br /&gt;
#* Сделаем список, состоящий из пар символов вида &amp;lt;tex&amp;gt; S[i..i + 1] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; i \bmod 2 = 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его цифровой сортировкой за линейное время и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S[1..n] &amp;lt;/tex&amp;gt; в алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;, получив строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; половинной длины.&lt;br /&gt;
# Рекурсивно построим суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Построим суффиксный массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;. Очевидно, &amp;lt;tex&amp;gt; A_{S_o}[i] = 2 A_{S'}[i] + 1 &amp;lt;/tex&amp;gt;, так отношение упорядоченности любых двух строк в старом алфавите &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt; эквивалентно отношению упорядоченности в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; по его построению.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для четных суффиксов'''&lt;br /&gt;
На этом шаге мы за линейное время получим суффиксный массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; для четных суффиксов, используя уже построенный &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что сортировка множества четных суффиксов &amp;lt;tex&amp;gt; \{ S[i..n] | i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке множества пар &amp;lt;tex&amp;gt; \{ (S[i], S[i+1..n]) | i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt;. Однако &amp;lt;tex&amp;gt; S[i+1..n] &amp;lt;/tex&amp;gt; — нечетный суффикс, и его относительную позицию мы уже узнали на шаге 1.&lt;br /&gt;
&lt;br /&gt;
Таким образом, чтобы отсортировать эти пары за линейное время, сначала сразу выпишем их в порядке возрастания второго элемента пары (то есть в порядке вхождения в массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;), а потом отсортируем устойчивой сортировкой подсчетом по первым элементам. После этого легко можно восстановить массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
     M.add(Pair(S[&amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
    &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&gt;
&lt;br /&gt;
Заметим, что массив &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; перед сортировкой подсчетом не был явно отсортирован по вторым элементам, и хранил не суффиксы, а их позиции в строке &amp;lt;tex&amp;gt; S &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;
Для суффиксного дерева третий шаг алгоритма опирается на специфические особенности суффиксных деревьев, которые не присущи суффиксным массивам&amp;lt;ref&amp;gt;M. Farach. Optimal suffix tree construction with large alphabets. http://www.cs.rutgers.edu/~farach/pubs/FarFerrMuthu00.pdf &amp;lt;/ref&amp;gt; .  &lt;br /&gt;
В случае суффиксного массива слияние становится очень сложным, но все же оно было реализовано в алгоритме Ким-Сим-Парк-Парка&amp;lt;ref&amp;gt; D. K. Kim, J. S. Sim, H. Park, and K. Park. Linear-time construction of suffix arrays. http://www.springerlink.com/content/568156021q45r320/&amp;lt;/ref&amp;gt;. Однако простой модификацией алгоритма можно значительно упростить его.&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
Покажем первые два шага агоритма для строки '''ababbbaa'''.&lt;br /&gt;
&lt;br /&gt;
Во-первых, добавив защитный символ '''$''', получив строку '''ababbbaa$''' (для этого алгоритма он не требуется, но может понадобиться в применениях суффиксного массива). Во-вторых, дополним ее до четной длины, получив '''ababbbaa$$'''.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для нечетных суффиксов'''&lt;br /&gt;
# В новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет четыре элемента — '''ba''', '''bb''', '''a$''', '''$$'''. После сортировки они получат номера 2, 3, 1 и 0 соответственно.&lt;br /&gt;
# Переводим строку S[1..n] = '''babbbaa$$$''' в новый алфавит. Сжатой строкой &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; будет '''23210'''.&lt;br /&gt;
# После рекурсивного вызова получим, что &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [4, 3, 2, 0, 1], и &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; = [9, 7, 5, 1, 3].&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для четных суффиксов'''&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''$''', 9), ('''a''', 7), ('''b''', 5), ('''a''', 1), ('''a''', 3)].&lt;br /&gt;
# После сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt;= [('''$''', 9), ('''a''', 7), ('''a''', 1), ('''a''', 3), ('''b''', 5)].&lt;br /&gt;
# Восстановив массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;, получаем [8, 6, 0, 2, 4], что действительно является суффиксным массивом для четных суффиксов.&lt;br /&gt;
&lt;br /&gt;
* '''Слияние суффиксных массивов'''&lt;br /&gt;
Если бы мы умели сливать &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; за линейное время, получили бы:&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 9 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; a\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; aa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ababbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; baa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; babbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Как и было сказано вначале, избавиться от лишних '''$''' легко, так как суффиксы, им соответствующие, будут первыми в суффиксном массиве (в данном случае достаточно выбросить &amp;quot;9&amp;quot; из суффиксного массива).&lt;br /&gt;
&lt;br /&gt;
== Алгоритм Карккайнена-Сандерса == &lt;br /&gt;
Изменим изначальный алгоритм следующим образом:&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих позициям, не кратным трем. Рекурсивно сведем это к построению суффиксного массива для строки длиной в две трети исходной.&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих кратным трем позициям, используя результат первого шага, за линейное время.&lt;br /&gt;
# Сольем эти суффиксные массивы в один за линейное время.&lt;br /&gt;
&lt;br /&gt;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left(\dfrac23 n\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого также является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt; (это видно из того, что сумма геометрической прогрессии с основанием &amp;lt;tex&amp;gt; \dfrac23 &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Аналогично первой версии алгоритма, дополним строку &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; до длины, кратной трем, защитными символами &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* '''База рекурсии'''&lt;br /&gt;
Для этого алгоритма минимальной базой рекурсии будет строка длиной 4, так как она дополняется до длины 6, после чего вновь следует рекурсивный вызов строки длиной 4, и, если бы база была меньше 4, алгоритм вошел бы в бесконечную рекурсию. На этом этапе также можно применить обычную сортировку суффиксов, так как это потребует &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
На этом шаге строится суффиксный массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; для множества суффиксов &amp;lt;tex&amp;gt; \{ S[i..n-1] | i \bmod 3 \ne 0 \} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Получим строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; аналогично предыдущему алгоритму:&lt;br /&gt;
#* Сделаем список, состоящий из троек &amp;lt;tex&amp;gt; S[i..i+2]&amp;lt;/tex&amp;gt; , где &amp;lt;tex&amp;gt; i \bmod 3 \ne 0 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его за линейное время цифровой сортировкой и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S[1..n]S[2..n+1] &amp;lt;/tex&amp;gt; в строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; длиной &amp;lt;tex&amp;gt; \dfrac23 n &amp;lt;/tex&amp;gt; в алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;. Тогда суффиксу &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt; в старом алфавите, где &amp;lt;tex&amp;gt; i \bmod 3 = 1 &amp;lt;/tex&amp;gt;, в новом алфавите будет соответствовать строка &amp;lt;tex&amp;gt; S'\left[\dfrac{i-1}{3}..\dfrac{n}{3} - 1\right] &amp;lt;/tex&amp;gt;, а если &amp;lt;tex&amp;gt; i\bmod 3 = 2 &amp;lt;/tex&amp;gt;, то строка &amp;lt;tex&amp;gt; S'\left[\dfrac{n}{3} + \dfrac{i-2}{3}..\dfrac{2n}{3} - 1\right] &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Вызовем алгоритм рекурсивно для строки &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt;, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Пройдем по массиву &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt; A_{S'}[i] &amp;lt; \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j = 3A_{S'}[i] + 1 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;, если же &amp;lt;tex&amp;gt; A_{S'}[i] \geqslant \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j =  3\left(A_{S'}[i] - \dfrac{n}{3}\right) + 2 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;. Псевдокод получения &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;.length - 1:&lt;br /&gt;
    '''if''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] &amp;lt; n/3:&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] + 1)&lt;br /&gt;
    '''else''':&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * (&amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] - n/3) + 2)&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
Этот шаг также аналогичен первой версии алгоритма. Сортировка множества &amp;lt;tex&amp;gt; \{ S[i..n-1] | i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке пар &amp;lt;tex&amp;gt; \{ (S[i], S[i+1..n-1]) | i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; S[i+1..n-1] &amp;lt;/tex&amp;gt; — суффиксы в позициях, равных 1 по модулю 3, относительный порядок которых уже известен. Выпишем эти пары в порядке вхождения их в &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и отсортируем по первому элементу устойчивой сортировкой подсчетом, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;. Псевдокод этого шага:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' 2n/3 - 1:&lt;br /&gt;
     '''if''' &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[i] % 3 == 1:&lt;br /&gt;
         M.add(Pair(S[&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 '''for''' i = 0 '''to''' n/3 - 1:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&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; A_{S_0} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, чтобы получить суффиксный массив &amp;lt;tex&amp;gt; A_{S} &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;
&lt;br /&gt;
Пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 1 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Это аналогично сравнению пар &amp;lt;tex&amp;gt; (S[i], S[i+1..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S[j], S[j+1..n-1]) &amp;lt;/tex&amp;gt;. Сравнить первые элементы пар мы можем за &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt;, а относительный порядок вторых элементов пар нам уже известен, так как они соответствуют позициям, равным 2 и 1 по модулю 3 соответственно.&lt;br /&gt;
&lt;br /&gt;
Аналогично, пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 2 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Тогда это аналогично сравнению троек &amp;lt;tex&amp;gt; (S[i], S[i+1], S[i+2..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S[j], S[j+1], S[j+2..n-1]) &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;A_{S}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 // Вначале предподсчитаем за O(n) обратную перестановку для суффиксного массива &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;, то есть массив rank такой, что &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[rank[i]] = i.&lt;br /&gt;
 // Тогда мы сможем за O(1) сравнивать суффиксы по их позиции.&lt;br /&gt;
 rank = inverse(&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;) &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3 '''and''' j &amp;lt; n/3:&lt;br /&gt;
     pos12 = &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i]&lt;br /&gt;
     pos0  = &amp;lt;tex&amp;gt; A_{0} &amp;lt;/tex&amp;gt;[j]&lt;br /&gt;
     '''if''' pos12 % 3 == 1:&lt;br /&gt;
        ''' if''' Pair(S[pos12], rank[pos12 + 1]) &amp;lt; Pair(S[pos0], rank[pos0 + 1]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++  &lt;br /&gt;
     '''else''':&lt;br /&gt;
         '''if''' Triple(S[pos12], S[pos12 + 1], rank[pos12 + 2]) &amp;lt; Triple(S[pos0], S[pos0 + 1], rank[pos0 + 2]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++ &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i])&lt;br /&gt;
     i++&lt;br /&gt;
 '''while''' j &amp;lt; n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{0}} &amp;lt;/tex&amp;gt;[j])&lt;br /&gt;
     j++&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;
Построим суффиксный массив для строки &amp;lt;tex&amp;gt;abbacab&amp;lt;/tex&amp;gt;. После добавления защитного символа и дополнения до кратной трем длины, получим &amp;lt;tex&amp;gt;abbacab$$&amp;lt;/tex&amp;gt;.&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
# Тройками, соответствующими равными 1 по модулю 3 позициям, будут: &amp;lt;tex&amp;gt;bba&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;cab&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;\$\$\$&amp;lt;/tex&amp;gt;, соответствующими равным 2 по модулю 3 — &amp;lt;tex&amp;gt;bac&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;ab\$&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;\$\$\$&amp;lt;/tex&amp;gt;. Новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет содержать элементы &amp;lt;tex&amp;gt;bba&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;cab&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;\$\$\$&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;bac&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;ab\$&amp;lt;/tex&amp;gt;, которые после сортировки получат номера 3, 4, 0, 2, 1 соответственно.&lt;br /&gt;
# Строкой &amp;lt;tex&amp;gt;bbacab\$\$\$bacab\$\$\$\$&amp;lt;/tex&amp;gt; в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; = 340210. &lt;br /&gt;
# После рекурсивного вызова получим &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [5, 2, 4, 3, 0, 1]. Пересчитав &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, получим [(5 - 3)*3 + 2, 2 * 3 + 1, (4 - 3) * 3 + 2, (3 - 3) * 3 + 2, 0 * 3 + 1, 1 * 3 + 1] = [8, 7, 5, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и выбрав в нем элементы, равные 1 по модулю 3, получим массив пар &amp;lt;tex&amp;gt;M&amp;lt;/tex&amp;gt; = [(&amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, 7), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 1), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 4)]&lt;br /&gt;
# После устойчивой сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [(&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 1), (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, 4), (&amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, 7)]&lt;br /&gt;
# Восстановив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;, получаем [0, 3, 6].&lt;br /&gt;
* '''Слияние суффиксных массивов'''&lt;br /&gt;
Рассмотрим, к примеру, третью итерацию слияния, к этой итерации массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 2, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0, на ней мы сливаем суффиксы, соответствующие позициям 5 и 0. &lt;br /&gt;
# Образуем тройки (S[5], S[6], S[7..8]) и (S[0], S[1], S[2..8]).&lt;br /&gt;
# После получения относительного порядка суффиксов, получим тройки (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, 1) и (&amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;b&amp;lt;/tex&amp;gt;, 3). Первая тройка меньше второй, поэтому добавляем суффикс, соответствующий позиции 5 в массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# В конце итерации получаем &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 3, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0.&lt;br /&gt;
К концу слияния получим &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5, 0, 3, 6, 2, 1, 4]. Так как мы добавляли один символ &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; в начале алгоритма для дополнения строки до длины, кратной трем, выбросим последний суффикс из &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;, получим в итоге, что &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [7, 5, 0, 3, 6, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; acab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; b\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; cab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
Заметим, что №9 будет выброшен, так как в начале алгоритма был добавлен один &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt; к строке&lt;br /&gt;
 &lt;br /&gt;
{|&lt;br /&gt;
| [[Файл:Kark_sanders_stage1.png|325px|thumb| Фаза 1]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage2.png|325px|thumb| Фаза 2]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage3.png|325px|thumb| Фаза 3]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Получение массива LCP ==&lt;br /&gt;
Массив LCP можно получить за линейное время [[Алгоритм_Касаи_и_др. | алгоритмом Касаи]].&lt;br /&gt;
&lt;br /&gt;
== Обобщение алгоритма ==&lt;br /&gt;
На самом деле, алгоритм можно обобщить&amp;lt;ref name=&amp;quot;generalisation&amp;quot;&amp;gt; Juha Kärkkäinen, Peter Sanders and Stefan Burkhardt. Linear work suffix array construction. http://www.cs.helsinki.fi/juha.karkkainen/publications/jacm05-revised.pdf &amp;lt;/ref&amp;gt;, взяв на первом шаге, к примеру, суффиксы, позиции которых по модулю 7 дают 3, 5 и 6. Для этого потребуются некоторое усложнение алгоритма, например, сортировка оставшихся суффиксов в нескольких группах на шаге 2 и слияние нескольких групп на шаге 3, но основная идея алгоритма остается той же. Множества, которые можно выбрать, на первом шаге определяются '''разностным покрытием''' (''difference cover'').&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Разностное покрытие''' (''difference cover'') &amp;lt;tex&amp;gt; D &amp;lt;/tex&amp;gt; по модулю &amp;lt;tex&amp;gt;m &amp;lt;/tex&amp;gt; — множество чисел от &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;m - 1 &amp;lt;/tex&amp;gt; таких, что &amp;lt;tex&amp;gt; \forall i \in [0, m-1]: \exists j, k \in D: i \equiv k - j \pmod m &amp;lt;/tex&amp;gt;.&lt;br /&gt;
}} &lt;br /&gt;
Например, &amp;lt;tex&amp;gt; \{1, 2\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt; \{3, 5, 6\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt; \{1\} &amp;lt;/tex&amp;gt; — не является разностным покрытием по модулю &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;, поэтому этот алгоритм не применим к нему. Подробнее узнать, как вычислять разностное покрытие для заданного модуля можно также здесь&amp;lt;ref name=&amp;quot;generalisation&amp;quot;/&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Источники информации==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
* [http://www.cs.helsinki.fi/juha.karkkainen/publications/icalp03.pdf Juha Kärkkäinen and Peter Sanders. Simple linear work suffix array construction]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Суффиксный массив]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=47933</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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=47933"/>
				<updated>2015-06-07T06:21:10Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Пример */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Алгоритм Карккайнена-Сандерса''' (Kärkkäinen, Sanders) — алгоритм построения [[суффиксный массив | суффиксного массива]] за линейное время.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Используемые обозначения ==&lt;br /&gt;
* В данном конспекте используется 0-индексация.&lt;br /&gt;
* &amp;lt;tex&amp;gt;S[i..j] &amp;lt;/tex&amp;gt; — подстрока строки &amp;lt;tex&amp;gt; S &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;-й символы включительно.&lt;br /&gt;
* Пусть длина строки &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;. Обозначим &amp;lt;tex&amp;gt; S[i..j] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; j \geqslant n &amp;lt;/tex&amp;gt; как строку &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt;, дополненную защитными символами &amp;lt;tex&amp;gt; \$ &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;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Четным суффиксом''' назовем суффикс, начинающийся в четной позиции. &amp;lt;br&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;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left( \dfrac{n}{2}\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого является &amp;lt;tex&amp;gt; T(n) = O(n) &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;
&lt;br /&gt;
* '''Суффиксный массив для нечетных суффиксов'''&lt;br /&gt;
&lt;br /&gt;
# Отобразим исходную строку &amp;lt;tex&amp;gt; S &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; длины  &amp;lt;tex&amp;gt; \dfrac{n}{2} &amp;lt;/tex&amp;gt;  следующим образом:&lt;br /&gt;
#* Сделаем список, состоящий из пар символов вида &amp;lt;tex&amp;gt; S[i..i + 1] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; i \bmod 2 = 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его цифровой сортировкой за линейное время и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S[1..n] &amp;lt;/tex&amp;gt; в алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;, получив строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; половинной длины.&lt;br /&gt;
# Рекурсивно построим суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Построим суффиксный массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;. Очевидно, &amp;lt;tex&amp;gt; A_{S_o}[i] = 2 A_{S'}[i] + 1 &amp;lt;/tex&amp;gt;, так отношение упорядоченности любых двух строк в старом алфавите &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt; эквивалентно отношению упорядоченности в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; по его построению.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для четных суффиксов'''&lt;br /&gt;
На этом шаге мы за линейное время получим суффиксный массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; для четных суффиксов, используя уже построенный &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что сортировка множества четных суффиксов &amp;lt;tex&amp;gt; \{ S[i..n] | i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке множества пар &amp;lt;tex&amp;gt; \{ (S[i], S[i+1..n]) | i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt;. Однако &amp;lt;tex&amp;gt; S[i+1..n] &amp;lt;/tex&amp;gt; — нечетный суффикс, и его относительную позицию мы уже узнали на шаге 1.&lt;br /&gt;
&lt;br /&gt;
Таким образом, чтобы отсортировать эти пары за линейное время, сначала сразу выпишем их в порядке возрастания второго элемента пары (то есть в порядке вхождения в массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;), а потом отсортируем устойчивой сортировкой подсчетом по первым элементам. После этого легко можно восстановить массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
     M.add(Pair(S[&amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
    &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&gt;
&lt;br /&gt;
Заметим, что массив &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; перед сортировкой подсчетом не был явно отсортирован по вторым элементам, и хранил не суффиксы, а их позиции в строке &amp;lt;tex&amp;gt; S &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;
Для суффиксного дерева третий шаг алгоритма опирается на специфические особенности суффиксных деревьев, которые не присущи суффиксным массивам&amp;lt;ref&amp;gt;M. Farach. Optimal suffix tree construction with large alphabets. http://www.cs.rutgers.edu/~farach/pubs/FarFerrMuthu00.pdf &amp;lt;/ref&amp;gt; .  &lt;br /&gt;
В случае суффиксного массива слияние становится очень сложным, но все же оно было реализовано в алгоритме Ким-Сим-Парк-Парка&amp;lt;ref&amp;gt; D. K. Kim, J. S. Sim, H. Park, and K. Park. Linear-time construction of suffix arrays. http://www.springerlink.com/content/568156021q45r320/&amp;lt;/ref&amp;gt;. Однако простой модификацией алгоритма можно значительно упростить его.&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
Покажем первые два шага агоритма для строки '''ababbbaa'''.&lt;br /&gt;
&lt;br /&gt;
Во-первых, добавив защитный символ '''$''', получив строку '''ababbbaa$''' (для этого алгоритма он не требуется, но может понадобиться в применениях суффиксного массива). Во-вторых, дополним ее до четной длины, получив '''ababbbaa$$'''.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для нечетных суффиксов'''&lt;br /&gt;
# В новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет четыре элемента — '''ba''', '''bb''', '''a$''', '''$$'''. После сортировки они получат номера 2, 3, 1 и 0 соответственно.&lt;br /&gt;
# Переводим строку S[1..n] = '''babbbaa$$$''' в новый алфавит. Сжатой строкой &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; будет '''23210'''.&lt;br /&gt;
# После рекурсивного вызова получим, что &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [4, 3, 2, 0, 1], и &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; = [9, 7, 5, 1, 3].&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для четных суффиксов'''&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''$''', 9), ('''a''', 7), ('''b''', 5), ('''a''', 1), ('''a''', 3)].&lt;br /&gt;
# После сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt;= [('''$''', 9), ('''a''', 7), ('''a''', 1), ('''a''', 3), ('''b''', 5)].&lt;br /&gt;
# Восстановив массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;, получаем [8, 6, 0, 2, 4], что действительно является суффиксным массивом для четных суффиксов.&lt;br /&gt;
&lt;br /&gt;
* '''Слияние суффиксных массивов'''&lt;br /&gt;
Если бы мы умели сливать &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; за линейное время, получили бы:&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 9 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; a\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; aa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ababbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; baa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; babbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Как и было сказано вначале, избавиться от лишних '''$''' легко, так как суффиксы, им соответствующие, будут первыми в суффиксном массиве (в данном случае достаточно выбросить &amp;quot;9&amp;quot; из суффиксного массива).&lt;br /&gt;
&lt;br /&gt;
== Алгоритм Карккайнена-Сандерса == &lt;br /&gt;
Изменим изначальный алгоритм следующим образом:&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих позициям, не кратным трем. Рекурсивно сведем это к построению суффиксного массива для строки длиной в две трети исходной.&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих кратным трем позициям, используя результат первого шага, за линейное время.&lt;br /&gt;
# Сольем эти суффиксные массивы в один за линейное время.&lt;br /&gt;
&lt;br /&gt;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left(\dfrac23 n\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого также является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt; (это видно из того, что сумма геометрической прогрессии с основанием &amp;lt;tex&amp;gt; \dfrac23 &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Аналогично первой версии алгоритма, дополним строку &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; до длины, кратной трем, защитными символами &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* '''База рекурсии'''&lt;br /&gt;
Для этого алгоритма минимальной базой рекурсии будет строка длиной 4, так как она дополняется до длины 6, после чего вновь следует рекурсивный вызов строки длиной 4, и, если бы база была меньше 4, алгоритм вошел бы в бесконечную рекурсию. На этом этапе также можно применить обычную сортировку суффиксов, так как это потребует &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
На этом шаге строится суффиксный массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; для множества суффиксов &amp;lt;tex&amp;gt; \{ S[i..n-1] | i \bmod 3 \ne 0 \} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Получим строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; аналогично предыдущему алгоритму:&lt;br /&gt;
#* Сделаем список, состоящий из троек &amp;lt;tex&amp;gt; S[i..i+2]&amp;lt;/tex&amp;gt; , где &amp;lt;tex&amp;gt; i \bmod 3 \ne 0 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его за линейное время цифровой сортировкой и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S[1..n]S[2..n+1] &amp;lt;/tex&amp;gt; в строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; длиной &amp;lt;tex&amp;gt; \dfrac23 n &amp;lt;/tex&amp;gt; в алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;. Тогда суффиксу &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt; в старом алфавите, где &amp;lt;tex&amp;gt; i \bmod 3 = 1 &amp;lt;/tex&amp;gt;, в новом алфавите будет соответствовать строка &amp;lt;tex&amp;gt; S'\left[\dfrac{i-1}{3}..\dfrac{n}{3} - 1\right] &amp;lt;/tex&amp;gt;, а если &amp;lt;tex&amp;gt; i\bmod 3 = 2 &amp;lt;/tex&amp;gt;, то строка &amp;lt;tex&amp;gt; S'\left[\dfrac{n}{3} + \dfrac{i-2}{3}..\dfrac{2n}{3} - 1\right] &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Вызовем алгоритм рекурсивно для строки &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt;, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Пройдем по массиву &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt; A_{S'}[i] &amp;lt; \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j = 3A_{S'}[i] + 1 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;, если же &amp;lt;tex&amp;gt; A_{S'}[i] \geqslant \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j =  3\left(A_{S'}[i] - \dfrac{n}{3}\right) + 2 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;. Псевдокод получения &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;.length - 1:&lt;br /&gt;
    '''if''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] &amp;lt; n/3:&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] + 1)&lt;br /&gt;
    '''else''':&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * (&amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] - n/3) + 2)&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
Этот шаг также аналогичен первой версии алгоритма. Сортировка множества &amp;lt;tex&amp;gt; \{ S[i..n-1] | i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке пар &amp;lt;tex&amp;gt; \{ (S[i], S[i+1..n-1]) | i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; S[i+1..n-1] &amp;lt;/tex&amp;gt; — суффиксы в позициях, равных 1 по модулю 3, относительный порядок которых уже известен. Выпишем эти пары в порядке вхождения их в &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и отсортируем по первому элементу устойчивой сортировкой подсчетом, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;. Псевдокод этого шага:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' 2n/3 - 1:&lt;br /&gt;
     '''if''' &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[i] % 3 == 1:&lt;br /&gt;
         M.add(Pair(S[&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 '''for''' i = 0 '''to''' n/3 - 1:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&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; A_{S_0} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, чтобы получить суффиксный массив &amp;lt;tex&amp;gt; A_{S} &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;
&lt;br /&gt;
Пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 1 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Это аналогично сравнению пар &amp;lt;tex&amp;gt; (S[i], S[i+1..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S[j], S[j+1..n-1]) &amp;lt;/tex&amp;gt;. Сравнить первые элементы пар мы можем за &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt;, а относительный порядок вторых элементов пар нам уже известен, так как они соответствуют позициям, равным 2 и 1 по модулю 3 соответственно.&lt;br /&gt;
&lt;br /&gt;
Аналогично, пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 2 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Тогда это аналогично сравнению троек &amp;lt;tex&amp;gt; (S[i], S[i+1], S[i+2..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S[j], S[j+1], S[j+2..n-1]) &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;A_{S}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 // Вначале предподсчитаем за O(n) обратную перестановку для суффиксного массива &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;, то есть массив rank такой, что &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[rank[i]] = i.&lt;br /&gt;
 // Тогда мы сможем за O(1) сравнивать суффиксы по их позиции.&lt;br /&gt;
 rank = inverse(&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;) &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3 '''and''' j &amp;lt; n/3:&lt;br /&gt;
     pos12 = &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i]&lt;br /&gt;
     pos0  = &amp;lt;tex&amp;gt; A_{0} &amp;lt;/tex&amp;gt;[j]&lt;br /&gt;
     '''if''' pos12 % 3 == 1:&lt;br /&gt;
        ''' if''' Pair(S[pos12], rank[pos12 + 1]) &amp;lt; Pair(S[pos0], rank[pos0 + 1]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++  &lt;br /&gt;
     '''else''':&lt;br /&gt;
         '''if''' Triple(S[pos12], S[pos12 + 1], rank[pos12 + 2]) &amp;lt; Triple(S[pos0], S[pos0 + 1], rank[pos0 + 2]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++ &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i])&lt;br /&gt;
     i++&lt;br /&gt;
 '''while''' j &amp;lt; n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{0}} &amp;lt;/tex&amp;gt;[j])&lt;br /&gt;
     j++&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;
Построим суффиксный массив для строки '''abbacab'''. После добавления защитного символа и дополнения до кратной трем длины, получим '''abbacab$$'''.&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
# Тройками, соответствующими равными 1 по модулю 3 позициям, будут: '''bba''', '''cab''', '''$$$''', соответствующими равным 2 по модулю 3 — '''bac''', '''ab$''', '''$$$'''. Новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет содержать элементы '''bba''', '''cab''', '''$$$''', '''bac''', '''ab$''', которые после сортировки получат номера 3, 4, 0, 2, 1 соотвествтенно.&lt;br /&gt;
# Строкой '''bbacab$$$bacab$$$$''' в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; = '''340210'''. &lt;br /&gt;
# После рекурсивного вызова получим &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [5, 2, 4, 3, 0, 1]. Пересчитав &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, получим [(5 - 3)*3 + 2, 2 * 3 + 1, (4 - 3) * 3 + 2, (3 - 3) * 3 + 2, 0 * 3 + 1, 1 * 3 + 1] = [8, 7, 5, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и выбрав в нем элементы, равные 1 по модулю 3, получим массив пар &amp;lt;tex&amp;gt;M&amp;lt;/tex&amp;gt; = [('''b''', 7), ('''a''', 1), ('''a''', 4)]&lt;br /&gt;
# После устойчивой сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''a''', 1), ('''a''', 4), ('''b''', 7)]&lt;br /&gt;
# Восстановив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;, получаем [0, 3, 6].&lt;br /&gt;
* '''Слияние суффиксных массивов'''&lt;br /&gt;
Рассмотрим, к примеру, третью итерацию слияния, к этой итерации массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 2, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0, на ней мы сливаем суффиксы, соответствующие позициям 5 и 0. &lt;br /&gt;
# Образуем тройки (S[5], S[6], S[7..8]) и (S[0], S[1], S[2..8]).&lt;br /&gt;
# После получения относительного порядка суффиксов, получим тройки ('''a''', '''b''', 1) и ('''a''', '''b''', 3). Первая тройка меньше второй, поэтому добавляем суффикс, соответствующий позиции 5 в массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# В конце итерации получаем &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 3, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0.&lt;br /&gt;
К концу слияния получим &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5, 0, 3, 6, 2, 1, 4]. Так как мы добавляли один символ '''$''' в начале алгоритма для дополнения строки до длины, кратной трем, выбросим последний суффикс из &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;, получим в итоге, что &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [7, 5, 0, 3, 6, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; acab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; b\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbacab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; cab\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
Заметим, что №9 будет выброшен, так как в начале алгоритма был добавлен один &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt; к строке&lt;br /&gt;
 &lt;br /&gt;
{|&lt;br /&gt;
| [[Файл:Kark_sanders_stage1.png|325px|thumb| Фаза 1]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage2.png|325px|thumb| Фаза 2]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage3.png|325px|thumb| Фаза 3]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Получение массива LCP ==&lt;br /&gt;
Массив LCP можно получить за линейное время [[Алгоритм_Касаи_и_др. | алгоритмом Касаи]].&lt;br /&gt;
&lt;br /&gt;
== Обобщение алгоритма ==&lt;br /&gt;
На самом деле, алгоритм можно обобщить&amp;lt;ref name=&amp;quot;generalisation&amp;quot;&amp;gt; Juha Kärkkäinen, Peter Sanders and Stefan Burkhardt. Linear work suffix array construction. http://www.cs.helsinki.fi/juha.karkkainen/publications/jacm05-revised.pdf &amp;lt;/ref&amp;gt;, взяв на первом шаге, к примеру, суффиксы, позиции которых по модулю 7 дают 3, 5 и 6. Для этого потребуются некоторое усложнение алгоритма, например, сортировка оставшихся суффиксов в нескольких группах на шаге 2 и слияние нескольких групп на шаге 3, но основная идея алгоритма остается той же. Множества, которые можно выбрать, на первом шаге определяются '''разностным покрытием''' (''difference cover'').&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Разностное покрытие''' (''difference cover'') &amp;lt;tex&amp;gt; D &amp;lt;/tex&amp;gt; по модулю &amp;lt;tex&amp;gt;m &amp;lt;/tex&amp;gt; — множество чисел от &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;m - 1 &amp;lt;/tex&amp;gt; таких, что &amp;lt;tex&amp;gt; \forall i \in [0, m-1]: \exists j, k \in D: i \equiv k - j \pmod m &amp;lt;/tex&amp;gt;.&lt;br /&gt;
}} &lt;br /&gt;
Например, &amp;lt;tex&amp;gt; \{1, 2\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt; \{3, 5, 6\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt; \{1\} &amp;lt;/tex&amp;gt; — не является разностным покрытием по модулю &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;, поэтому этот алгоритм не применим к нему. Подробнее узнать, как вычислять разностное покрытие для заданного модуля можно также здесь&amp;lt;ref name=&amp;quot;generalisation&amp;quot;/&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Источники информации==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
* [http://www.cs.helsinki.fi/juha.karkkainen/publications/icalp03.pdf Juha Kärkkäinen and Peter Sanders. Simple linear work suffix array construction]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Суффиксный массив]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=47932</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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=47932"/>
				<updated>2015-06-07T06:12:03Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Пример */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Алгоритм Карккайнена-Сандерса''' (Kärkkäinen, Sanders) — алгоритм построения [[суффиксный массив | суффиксного массива]] за линейное время.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Используемые обозначения ==&lt;br /&gt;
* В данном конспекте используется 0-индексация.&lt;br /&gt;
* &amp;lt;tex&amp;gt;S[i..j] &amp;lt;/tex&amp;gt; — подстрока строки &amp;lt;tex&amp;gt; S &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;-й символы включительно.&lt;br /&gt;
* Пусть длина строки &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;. Обозначим &amp;lt;tex&amp;gt; S[i..j] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; j \geqslant n &amp;lt;/tex&amp;gt; как строку &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt;, дополненную защитными символами &amp;lt;tex&amp;gt; \$ &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;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Четным суффиксом''' назовем суффикс, начинающийся в четной позиции. &amp;lt;br&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;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left( \dfrac{n}{2}\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого является &amp;lt;tex&amp;gt; T(n) = O(n) &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;
&lt;br /&gt;
* '''Суффиксный массив для нечетных суффиксов'''&lt;br /&gt;
&lt;br /&gt;
# Отобразим исходную строку &amp;lt;tex&amp;gt; S &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; длины  &amp;lt;tex&amp;gt; \dfrac{n}{2} &amp;lt;/tex&amp;gt;  следующим образом:&lt;br /&gt;
#* Сделаем список, состоящий из пар символов вида &amp;lt;tex&amp;gt; S[i..i + 1] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; i \bmod 2 = 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его цифровой сортировкой за линейное время и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S[1..n] &amp;lt;/tex&amp;gt; в алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;, получив строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; половинной длины.&lt;br /&gt;
# Рекурсивно построим суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Построим суффиксный массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;. Очевидно, &amp;lt;tex&amp;gt; A_{S_o}[i] = 2 A_{S'}[i] + 1 &amp;lt;/tex&amp;gt;, так отношение упорядоченности любых двух строк в старом алфавите &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt; эквивалентно отношению упорядоченности в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; по его построению.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для четных суффиксов'''&lt;br /&gt;
На этом шаге мы за линейное время получим суффиксный массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; для четных суффиксов, используя уже построенный &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что сортировка множества четных суффиксов &amp;lt;tex&amp;gt; \{ S[i..n] | i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке множества пар &amp;lt;tex&amp;gt; \{ (S[i], S[i+1..n]) | i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt;. Однако &amp;lt;tex&amp;gt; S[i+1..n] &amp;lt;/tex&amp;gt; — нечетный суффикс, и его относительную позицию мы уже узнали на шаге 1.&lt;br /&gt;
&lt;br /&gt;
Таким образом, чтобы отсортировать эти пары за линейное время, сначала сразу выпишем их в порядке возрастания второго элемента пары (то есть в порядке вхождения в массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;), а потом отсортируем устойчивой сортировкой подсчетом по первым элементам. После этого легко можно восстановить массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
     M.add(Pair(S[&amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
    &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&gt;
&lt;br /&gt;
Заметим, что массив &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; перед сортировкой подсчетом не был явно отсортирован по вторым элементам, и хранил не суффиксы, а их позиции в строке &amp;lt;tex&amp;gt; S &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;
Для суффиксного дерева третий шаг алгоритма опирается на специфические особенности суффиксных деревьев, которые не присущи суффиксным массивам&amp;lt;ref&amp;gt;M. Farach. Optimal suffix tree construction with large alphabets. http://www.cs.rutgers.edu/~farach/pubs/FarFerrMuthu00.pdf &amp;lt;/ref&amp;gt; .  &lt;br /&gt;
В случае суффиксного массива слияние становится очень сложным, но все же оно было реализовано в алгоритме Ким-Сим-Парк-Парка&amp;lt;ref&amp;gt; D. K. Kim, J. S. Sim, H. Park, and K. Park. Linear-time construction of suffix arrays. http://www.springerlink.com/content/568156021q45r320/&amp;lt;/ref&amp;gt;. Однако простой модификацией алгоритма можно значительно упростить его.&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
Покажем первые два шага агоритма для строки '''ababbbaa'''.&lt;br /&gt;
&lt;br /&gt;
Во-первых, добавив защитный символ '''$''', получив строку '''ababbbaa$''' (для этого алгоритма он не требуется, но может понадобиться в применениях суффиксного массива). Во-вторых, дополним ее до четной длины, получив '''ababbbaa$$'''.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для нечетных суффиксов'''&lt;br /&gt;
# В новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет четыре элемента — '''ba''', '''bb''', '''a$''', '''$$'''. После сортировки они получат номера 2, 3, 1 и 0 соответственно.&lt;br /&gt;
# Переводим строку S[1..n] = '''babbbaa$$$''' в новый алфавит. Сжатой строкой &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; будет '''23210'''.&lt;br /&gt;
# После рекурсивного вызова получим, что &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [4, 3, 2, 0, 1], и &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; = [9, 7, 5, 1, 3].&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для четных суффиксов'''&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''$''', 9), ('''a''', 7), ('''b''', 5), ('''a''', 1), ('''a''', 3)].&lt;br /&gt;
# После сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt;= [('''$''', 9), ('''a''', 7), ('''a''', 1), ('''a''', 3), ('''b''', 5)].&lt;br /&gt;
# Восстановив массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;, получаем [8, 6, 0, 2, 4], что действительно является суффиксным массивом для четных суффиксов.&lt;br /&gt;
&lt;br /&gt;
* '''Слияние суффиксных массивов'''&lt;br /&gt;
Если бы мы умели сливать &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; за линейное время, получили бы:&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;background-color:#CCC;margin:0.5px&amp;quot;&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| №&lt;br /&gt;
!style=&amp;quot;background-color:#EEE&amp;quot;| Подстрока&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 9 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 8 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; \$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; a\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 6 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; aa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 0 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; ababbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; abbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 5 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; baa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 1 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; babbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 4 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;&lt;br /&gt;
|style=&amp;quot;background-color:#FFF;padding:2px 30px&amp;quot;| &amp;lt;tex&amp;gt; bbbaa\$\$ &amp;lt;/tex&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Как и было сказано вначале, избавиться от лишних '''$''' легко, так как суффиксы, им соответствующие, будут первыми в суффиксном массиве (в данном случае достаточно выбросить &amp;quot;9&amp;quot; из суффиксного массива).&lt;br /&gt;
&lt;br /&gt;
== Алгоритм Карккайнена-Сандерса == &lt;br /&gt;
Изменим изначальный алгоритм следующим образом:&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих позициям, не кратным трем. Рекурсивно сведем это к построению суффиксного массива для строки длиной в две трети исходной.&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих кратным трем позициям, используя результат первого шага, за линейное время.&lt;br /&gt;
# Сольем эти суффиксные массивы в один за линейное время.&lt;br /&gt;
&lt;br /&gt;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left(\dfrac23 n\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого также является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt; (это видно из того, что сумма геометрической прогрессии с основанием &amp;lt;tex&amp;gt; \dfrac23 &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Аналогично первой версии алгоритма, дополним строку &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; до длины, кратной трем, защитными символами &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* '''База рекурсии'''&lt;br /&gt;
Для этого алгоритма минимальной базой рекурсии будет строка длиной 4, так как она дополняется до длины 6, после чего вновь следует рекурсивный вызов строки длиной 4, и, если бы база была меньше 4, алгоритм вошел бы в бесконечную рекурсию. На этом этапе также можно применить обычную сортировку суффиксов, так как это потребует &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
На этом шаге строится суффиксный массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; для множества суффиксов &amp;lt;tex&amp;gt; \{ S[i..n-1] | i \bmod 3 \ne 0 \} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Получим строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; аналогично предыдущему алгоритму:&lt;br /&gt;
#* Сделаем список, состоящий из троек &amp;lt;tex&amp;gt; S[i..i+2]&amp;lt;/tex&amp;gt; , где &amp;lt;tex&amp;gt; i \bmod 3 \ne 0 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его за линейное время цифровой сортировкой и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S[1..n]S[2..n+1] &amp;lt;/tex&amp;gt; в строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; длиной &amp;lt;tex&amp;gt; \dfrac23 n &amp;lt;/tex&amp;gt; в алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;. Тогда суффиксу &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt; в старом алфавите, где &amp;lt;tex&amp;gt; i \bmod 3 = 1 &amp;lt;/tex&amp;gt;, в новом алфавите будет соответствовать строка &amp;lt;tex&amp;gt; S'\left[\dfrac{i-1}{3}..\dfrac{n}{3} - 1\right] &amp;lt;/tex&amp;gt;, а если &amp;lt;tex&amp;gt; i\bmod 3 = 2 &amp;lt;/tex&amp;gt;, то строка &amp;lt;tex&amp;gt; S'\left[\dfrac{n}{3} + \dfrac{i-2}{3}..\dfrac{2n}{3} - 1\right] &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Вызовем алгоритм рекурсивно для строки &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt;, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Пройдем по массиву &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt; A_{S'}[i] &amp;lt; \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j = 3A_{S'}[i] + 1 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;, если же &amp;lt;tex&amp;gt; A_{S'}[i] \geqslant \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j =  3\left(A_{S'}[i] - \dfrac{n}{3}\right) + 2 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;. Псевдокод получения &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;.length - 1:&lt;br /&gt;
    '''if''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] &amp;lt; n/3:&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] + 1)&lt;br /&gt;
    '''else''':&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * (&amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] - n/3) + 2)&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
Этот шаг также аналогичен первой версии алгоритма. Сортировка множества &amp;lt;tex&amp;gt; \{ S[i..n-1] | i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке пар &amp;lt;tex&amp;gt; \{ (S[i], S[i+1..n-1]) | i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; S[i+1..n-1] &amp;lt;/tex&amp;gt; — суффиксы в позициях, равных 1 по модулю 3, относительный порядок которых уже известен. Выпишем эти пары в порядке вхождения их в &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и отсортируем по первому элементу устойчивой сортировкой подсчетом, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;. Псевдокод этого шага:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' 2n/3 - 1:&lt;br /&gt;
     '''if''' &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[i] % 3 == 1:&lt;br /&gt;
         M.add(Pair(S[&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 '''for''' i = 0 '''to''' n/3 - 1:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&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; A_{S_0} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, чтобы получить суффиксный массив &amp;lt;tex&amp;gt; A_{S} &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;
&lt;br /&gt;
Пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 1 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Это аналогично сравнению пар &amp;lt;tex&amp;gt; (S[i], S[i+1..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S[j], S[j+1..n-1]) &amp;lt;/tex&amp;gt;. Сравнить первые элементы пар мы можем за &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt;, а относительный порядок вторых элементов пар нам уже известен, так как они соответствуют позициям, равным 2 и 1 по модулю 3 соответственно.&lt;br /&gt;
&lt;br /&gt;
Аналогично, пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 2 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Тогда это аналогично сравнению троек &amp;lt;tex&amp;gt; (S[i], S[i+1], S[i+2..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S[j], S[j+1], S[j+2..n-1]) &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;A_{S}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 // Вначале предподсчитаем за O(n) обратную перестановку для суффиксного массива &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;, то есть массив rank такой, что &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[rank[i]] = i.&lt;br /&gt;
 // Тогда мы сможем за O(1) сравнивать суффиксы по их позиции.&lt;br /&gt;
 rank = inverse(&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;) &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3 '''and''' j &amp;lt; n/3:&lt;br /&gt;
     pos12 = &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i]&lt;br /&gt;
     pos0  = &amp;lt;tex&amp;gt; A_{0} &amp;lt;/tex&amp;gt;[j]&lt;br /&gt;
     '''if''' pos12 % 3 == 1:&lt;br /&gt;
        ''' if''' Pair(S[pos12], rank[pos12 + 1]) &amp;lt; Pair(S[pos0], rank[pos0 + 1]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++  &lt;br /&gt;
     '''else''':&lt;br /&gt;
         '''if''' Triple(S[pos12], S[pos12 + 1], rank[pos12 + 2]) &amp;lt; Triple(S[pos0], S[pos0 + 1], rank[pos0 + 2]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++ &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i])&lt;br /&gt;
     i++&lt;br /&gt;
 '''while''' j &amp;lt; n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{0}} &amp;lt;/tex&amp;gt;[j])&lt;br /&gt;
     j++&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;
Построим суффиксный массив для строки '''abbacab'''. После добавления защитного символа и дополнения до кратной трем длины, получим '''abbacab$$'''.&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
# Тройками, соответствующими равными 1 по модулю 3 позициям, будут: '''bba''', '''cab''', '''$$$''', соответствующими равным 2 по модулю 3 — '''bac''', '''ab$''', '''$$$'''. Новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет содержать элементы '''bba''', '''cab''', '''$$$''', '''bac''', '''ab$''', которые после сортировки получат номера 3, 4, 0, 2, 1 соотвествтенно.&lt;br /&gt;
# Строкой '''bbacab$$$bacab$$$$''' в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; = '''340210'''. &lt;br /&gt;
# После рекурсивного вызова получим &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [5, 2, 4, 3, 0, 1]. Пересчитав &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, получим [(5 - 3)*3 + 2, 2 * 3 + 1, (4 - 3) * 3 + 2, (3 - 3) * 3 + 2, 0 * 3 + 1, 1 * 3 + 1] = [8, 7, 5, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и выбрав в нем элементы, равные 1 по модулю 3, получим массив пар &amp;lt;tex&amp;gt;M&amp;lt;/tex&amp;gt; = [('''b''', 7), ('''a''', 1), ('''a''', 4)]&lt;br /&gt;
# После устойчивой сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''a''', 1), ('''a''', 4), ('''b''', 7)]&lt;br /&gt;
# Восстановив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;, получаем [0, 3, 6].&lt;br /&gt;
* '''Слияние суффиксных массивов'''&lt;br /&gt;
Рассмотрим, к примеру, третью итерацию слияния, к этой итерации массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 2, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0, на ней мы сливаем суффиксы, соответствующие позициям 5 и 0. &lt;br /&gt;
# Образуем тройки (S[5], S[6], S[7..8]) и (S[0], S[1], S[2..8]).&lt;br /&gt;
# После получения относительного порядка суффиксов, получим тройки ('''a''', '''b''', 1) и ('''a''', '''b''', 3). Первая тройка меньше второй, поэтому добавляем суффикс, соответствующий позиции 5 в массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# В конце итерации получаем &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 3, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0.&lt;br /&gt;
К концу слияния получим &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5, 0, 3, 6, 2, 1, 4]. Так как мы добавляли один символ '''$''' в начале алгоритма для дополнения строки до длины, кратной трем, выбросим последний суффикс из &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;, получим в итоге, что &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [7, 5, 0, 3, 6, 2, 1, 4].&lt;br /&gt;
 8 '''$''' (будет выброшен, так как в начале алгоритма добавляли один '''$''' к строке)&lt;br /&gt;
 7 '''$$'''&lt;br /&gt;
 5 '''ab$$'''&lt;br /&gt;
 0 '''abbacab$$'''&lt;br /&gt;
 3 '''acab$$'''&lt;br /&gt;
 6 '''b$$'''&lt;br /&gt;
 2 '''bacab$$'''&lt;br /&gt;
 1 '''bbacab$$'''&lt;br /&gt;
 4 '''cab$$'''&lt;br /&gt;
{|&lt;br /&gt;
| [[Файл:Kark_sanders_stage1.png|325px|thumb| Фаза 1]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage2.png|325px|thumb| Фаза 2]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage3.png|325px|thumb| Фаза 3]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Получение массива LCP ==&lt;br /&gt;
Массив LCP можно получить за линейное время [[Алгоритм_Касаи_и_др. | алгоритмом Касаи]].&lt;br /&gt;
&lt;br /&gt;
== Обобщение алгоритма ==&lt;br /&gt;
На самом деле, алгоритм можно обобщить&amp;lt;ref name=&amp;quot;generalisation&amp;quot;&amp;gt; Juha Kärkkäinen, Peter Sanders and Stefan Burkhardt. Linear work suffix array construction. http://www.cs.helsinki.fi/juha.karkkainen/publications/jacm05-revised.pdf &amp;lt;/ref&amp;gt;, взяв на первом шаге, к примеру, суффиксы, позиции которых по модулю 7 дают 3, 5 и 6. Для этого потребуются некоторое усложнение алгоритма, например, сортировка оставшихся суффиксов в нескольких группах на шаге 2 и слияние нескольких групп на шаге 3, но основная идея алгоритма остается той же. Множества, которые можно выбрать, на первом шаге определяются '''разностным покрытием''' (''difference cover'').&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Разностное покрытие''' (''difference cover'') &amp;lt;tex&amp;gt; D &amp;lt;/tex&amp;gt; по модулю &amp;lt;tex&amp;gt;m &amp;lt;/tex&amp;gt; — множество чисел от &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;m - 1 &amp;lt;/tex&amp;gt; таких, что &amp;lt;tex&amp;gt; \forall i \in [0, m-1]: \exists j, k \in D: i \equiv k - j \pmod m &amp;lt;/tex&amp;gt;.&lt;br /&gt;
}} &lt;br /&gt;
Например, &amp;lt;tex&amp;gt; \{1, 2\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt; \{3, 5, 6\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt; \{1\} &amp;lt;/tex&amp;gt; — не является разностным покрытием по модулю &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;, поэтому этот алгоритм не применим к нему. Подробнее узнать, как вычислять разностное покрытие для заданного модуля можно также здесь&amp;lt;ref name=&amp;quot;generalisation&amp;quot;/&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Источники информации==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
* [http://www.cs.helsinki.fi/juha.karkkainen/publications/icalp03.pdf Juha Kärkkäinen and Peter Sanders. Simple linear work suffix array construction]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Суффиксный массив]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=47921</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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=47921"/>
				<updated>2015-06-06T21:21:10Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Алгоритм Карккайнена-Сандерса */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Алгоритм Карккайнена-Сандерса''' (Kärkkäinen, Sanders) — алгоритм построения [[суффиксный массив | суффиксного массива]] за линейное время.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Используемые обозначения ==&lt;br /&gt;
* В данном конспекте используется 0-индексация.&lt;br /&gt;
* &amp;lt;tex&amp;gt;S[i..j] &amp;lt;/tex&amp;gt; — подстрока строки &amp;lt;tex&amp;gt; S &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;-й символы включительно.&lt;br /&gt;
* Пусть длина строки &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;. Обозначим &amp;lt;tex&amp;gt; S[i..j] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; j \geqslant n &amp;lt;/tex&amp;gt; как строку &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt;, дополненную защитными символами &amp;lt;tex&amp;gt; \$ &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;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Четным суффиксом''' назовем суффикс, начинающийся в четной позиции. &amp;lt;br&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;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left( \dfrac{n}{2}\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого является &amp;lt;tex&amp;gt; T(n) = O(n) &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;
&lt;br /&gt;
* '''Суффиксный массив для нечетных суффиксов'''&lt;br /&gt;
&lt;br /&gt;
# Отобразим исходную строку &amp;lt;tex&amp;gt; S &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; длины  &amp;lt;tex&amp;gt; \dfrac{n}{2} &amp;lt;/tex&amp;gt;  следующим образом:&lt;br /&gt;
#* Сделаем список, состоящий из пар символов вида &amp;lt;tex&amp;gt; S[i..i + 1] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; i \bmod 2 = 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его цифровой сортировкой за линейное время и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S[1..n] &amp;lt;/tex&amp;gt; в алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;, получив строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; половинной длины.&lt;br /&gt;
# Рекурсивно построим суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Построим суффиксный массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;. Очевидно, &amp;lt;tex&amp;gt; A_{S_o}[i] = 2 A_{S'}[i] + 1 &amp;lt;/tex&amp;gt;, так отношение упорядоченности любых двух строк в старом алфавите &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt; эквивалентно отношению упорядоченности в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; по его построению.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для четных суффиксов'''&lt;br /&gt;
На этом шаге мы за линейное время получим суффиксный массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; для четных суффиксов, используя уже построенный &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что сортировка множества четных суффиксов &amp;lt;tex&amp;gt; \{ S[i..n] | i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке множества пар &amp;lt;tex&amp;gt; \{ (S[i], S[i+1..n]) | i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt;. Однако &amp;lt;tex&amp;gt; S[i+1..n] &amp;lt;/tex&amp;gt; — нечетный суффикс, и его относительную позицию мы уже узнали на шаге 1.&lt;br /&gt;
&lt;br /&gt;
Таким образом, чтобы отсортировать эти пары за линейное время, сначала сразу выпишем их в порядке возрастания второго элемента пары (то есть в порядке вхождения в массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;), а потом отсортируем устойчивой сортировкой подсчетом по первым элементам. После этого легко можно восстановить массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
     M.add(Pair(S[&amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
    &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&gt;
&lt;br /&gt;
Заметим, что массив &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; перед сортировкой подсчетом не был явно отсортирован по вторым элементам, и хранил не суффиксы, а их позиции в строке &amp;lt;tex&amp;gt; S &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;
Для суффиксного дерева третий шаг алгоритма опирается на специфические особенности суффиксных деревьев, которые не присущи суффиксным массивам&amp;lt;ref&amp;gt;M. Farach. Optimal suffix tree construction with large alphabets. http://www.cs.rutgers.edu/~farach/pubs/FarFerrMuthu00.pdf &amp;lt;/ref&amp;gt; .  &lt;br /&gt;
В случае суффиксного массива слияние становится очень сложным, но все же оно было реализовано в алгоритме Ким-Сим-Парк-Парка&amp;lt;ref&amp;gt; D. K. Kim, J. S. Sim, H. Park, and K. Park. Linear-time construction of suffix arrays. http://www.springerlink.com/content/568156021q45r320/&amp;lt;/ref&amp;gt;. Однако простой модификацией алгоритма можно значительно упростить его.&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
Покажем первые два шага агоритма для строки '''ababbbaa'''.&lt;br /&gt;
&lt;br /&gt;
Во-первых, добавив защитный символ '''$''', получив строку '''ababbbaa$''' (для этого алгоритма он не требуется, но может понадобиться в применениях суффиксного массива). Во-вторых, дополним ее до четной длины, получив '''ababbbaa$$'''.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для нечетных суффиксов'''&lt;br /&gt;
# В новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет четыре элемента — '''ba''', '''bb''', '''a$''', '''$$'''. После сортировки они получат номера 2, 3, 1 и 0 соответственно.&lt;br /&gt;
# Переводим строку S[1..n] = '''babbbaa$$$''' в новый алфавит. Сжатой строкой &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; будет '''23210'''.&lt;br /&gt;
# После рекурсивного вызова получим, что &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [4, 3, 2, 0, 1], и &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; = [9, 7, 5, 1, 3].&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для четных суффиксов'''&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''$''', 9), ('''a''', 7), ('''b''', 5), ('''a''', 1), ('''a''', 3)].&lt;br /&gt;
# После сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt;= [('''$''', 9), ('''a''', 7), ('''a''', 1), ('''a''', 3), ('''b''', 5)].&lt;br /&gt;
# Восстановив массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;, получаем [8, 6, 0, 2, 4], что действительно является суффиксным массивом для четных суффиксов.&lt;br /&gt;
&lt;br /&gt;
* '''Слияние суффиксных массивов'''&lt;br /&gt;
Если бы мы умели сливать &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; за линейное время, получили бы:&lt;br /&gt;
 9 '''$'''&lt;br /&gt;
 8 '''$$'''&lt;br /&gt;
 7 '''a$$'''&lt;br /&gt;
 6 '''aa$$'''&lt;br /&gt;
 0 '''ababbbaa$$'''&lt;br /&gt;
 2 '''abbbaa$$'''&lt;br /&gt;
 5 '''baa$$'''&lt;br /&gt;
 1 '''babbbaa$$'''&lt;br /&gt;
 4 '''bbaa$$'''&lt;br /&gt;
 3 '''bbbaa$$'''&lt;br /&gt;
&lt;br /&gt;
Как и было сказано вначале, избавиться от лишних '''$''' легко, так как суффиксы, им соответствующие, будут первыми в суффиксном массиве (в данном случае достаточно выбросить &amp;quot;9&amp;quot; из суффиксного массива).&lt;br /&gt;
&lt;br /&gt;
== Алгоритм Карккайнена-Сандерса == &lt;br /&gt;
Изменим изначальный алгоритм следующим образом:&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих позициям, не кратным трем. Рекурсивно сведем это к построению суффиксного массива для строки длиной в две трети исходной.&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих кратным трем позициям, используя результат первого шага, за линейное время.&lt;br /&gt;
# Сольем эти суффиксные массивы в один за линейное время.&lt;br /&gt;
&lt;br /&gt;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left(\dfrac23 n\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого также является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt; (это видно из того, что сумма геометрической прогрессии с основанием &amp;lt;tex&amp;gt; \dfrac23 &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Аналогично первой версии алгоритма, дополним строку &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; до длины, кратной трем, защитными символами &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* '''База рекурсии'''&lt;br /&gt;
Для этого алгоритма минимальной базой рекурсии будет строка длиной 4, так как она дополняется до длины 6, после чего вновь следует рекурсивный вызов строки длиной 4, и, если бы база была меньше 4, алгоритм вошел бы в бесконечную рекурсию. На этом этапе также можно применить обычную сортировку суффиксов, так как это потребует &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
На этом шаге строится суффиксный массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; для множества суффиксов &amp;lt;tex&amp;gt; \{ S[i..n-1] | i \bmod 3 \ne 0 \} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Получим строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; аналогично предыдущему алгоритму:&lt;br /&gt;
#* Сделаем список, состоящий из троек &amp;lt;tex&amp;gt; S[i..i+2]&amp;lt;/tex&amp;gt; , где &amp;lt;tex&amp;gt; i \bmod 3 \ne 0 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его за линейное время цифровой сортировкой и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S[1..n]S[2..n+1] &amp;lt;/tex&amp;gt; в строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; длиной &amp;lt;tex&amp;gt; \dfrac23 n &amp;lt;/tex&amp;gt; в алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;. Тогда суффиксу &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt; в старом алфавите, где &amp;lt;tex&amp;gt; i \bmod 3 = 1 &amp;lt;/tex&amp;gt;, в новом алфавите будет соответствовать строка &amp;lt;tex&amp;gt; S'\left[\dfrac{i-1}{3}..\dfrac{n}{3} - 1\right] &amp;lt;/tex&amp;gt;, а если &amp;lt;tex&amp;gt; i\bmod 3 = 2 &amp;lt;/tex&amp;gt;, то строка &amp;lt;tex&amp;gt; S'\left[\dfrac{n}{3} + \dfrac{i-2}{3}..\dfrac{2n}{3} - 1\right] &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Вызовем алгоритм рекурсивно для строки &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt;, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Пройдем по массиву &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt; A_{S'}[i] &amp;lt; \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j = 3A_{S'}[i] + 1 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;, если же &amp;lt;tex&amp;gt; A_{S'}[i] \geqslant \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j =  3\left(A_{S'}[i] - \dfrac{n}{3}\right) + 2 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;. Псевдокод получения &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;.length - 1:&lt;br /&gt;
    '''if''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] &amp;lt; n/3:&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] + 1)&lt;br /&gt;
    '''else''':&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * (&amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] - n/3) + 2)&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
Этот шаг также аналогичен первой версии алгоритма. Сортировка множества &amp;lt;tex&amp;gt; \{ S[i..n-1] | i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке пар &amp;lt;tex&amp;gt; \{ (S[i], S[i+1..n-1]) | i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; S[i+1..n-1] &amp;lt;/tex&amp;gt; — суффиксы в позициях, равных 1 по модулю 3, относительный порядок которых уже известен. Выпишем эти пары в порядке вхождения их в &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и отсортируем по первому элементу устойчивой сортировкой подсчетом, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;. Псевдокод этого шага:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' 2n/3 - 1:&lt;br /&gt;
     '''if''' &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[i] % 3 == 1:&lt;br /&gt;
         M.add(Pair(S[&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 '''for''' i = 0 '''to''' n/3 - 1:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&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; A_{S_0} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, чтобы получить суффиксный массив &amp;lt;tex&amp;gt; A_{S} &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;
&lt;br /&gt;
Пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 1 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Это аналогично сравнению пар &amp;lt;tex&amp;gt; (S[i], S[i+1..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S[j], S[j+1..n-1]) &amp;lt;/tex&amp;gt;. Сравнить первые элементы пар мы можем за &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt;, а относительный порядок вторых элементов пар нам уже известен, так как они соответствуют позициям, равным 2 и 1 по модулю 3 соответственно.&lt;br /&gt;
&lt;br /&gt;
Аналогично, пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 2 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Тогда это аналогично сравнению троек &amp;lt;tex&amp;gt; (S[i], S[i+1], S[i+2..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S[j], S[j+1], S[j+2..n-1]) &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;A_{S}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 // Вначале предподсчитаем за O(n) обратную перестановку для суффиксного массива &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;, то есть массив rank такой, что &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[rank[i]] = i.&lt;br /&gt;
 // Тогда мы сможем за O(1) сравнивать суффиксы по их позиции.&lt;br /&gt;
 rank = inverse(&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;) &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3 '''and''' j &amp;lt; n/3:&lt;br /&gt;
     pos12 = &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i]&lt;br /&gt;
     pos0  = &amp;lt;tex&amp;gt; A_{0} &amp;lt;/tex&amp;gt;[j]&lt;br /&gt;
     '''if''' pos12 % 3 == 1:&lt;br /&gt;
        ''' if''' Pair(S[pos12], rank[pos12 + 1]) &amp;lt; Pair(S[pos0], rank[pos0 + 1]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++  &lt;br /&gt;
     '''else''':&lt;br /&gt;
         '''if''' Triple(S[pos12], S[pos12 + 1], rank[pos12 + 2]) &amp;lt; Triple(S[pos0], S[pos0 + 1], rank[pos0 + 2]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++ &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i])&lt;br /&gt;
     i++&lt;br /&gt;
 '''while''' j &amp;lt; n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{0}} &amp;lt;/tex&amp;gt;[j])&lt;br /&gt;
     j++&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;
Построим суффиксный массив для строки '''abbacab'''. После добавления защитного символа и дополнения до кратной трем длины, получим '''abbacab$$'''.&lt;br /&gt;
* '''Суффиксный массив для позиций не кратных 3'''&lt;br /&gt;
# Тройками, соответствующими равными 1 по модулю 3 позициям, будут: '''bba''', '''cab''', '''$$$''', соответствующими равным 2 по модулю 3 — '''bac''', '''ab$''', '''$$$'''. Новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет содержать элементы '''bba''', '''cab''', '''$$$''', '''bac''', '''ab$''', которые после сортировки получат номера 3, 4, 0, 2, 1 соотвествтенно.&lt;br /&gt;
# Строкой '''bbacab$$$bacab$$$$''' в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; = '''340210'''. &lt;br /&gt;
# После рекурсивного вызова получим &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [5, 2, 4, 3, 0, 1]. Пересчитав &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, получим [(5 - 3)*3 + 2, 2 * 3 + 1, (4 - 3) * 3 + 2, (3 - 3) * 3 + 2, 0 * 3 + 1, 1 * 3 + 1] = [8, 7, 5, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для позиций кратных 3'''&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и выбрав в нем элементы, равные 1 по модулю 3, получим массив пар &amp;lt;tex&amp;gt;M&amp;lt;/tex&amp;gt; = [('''b''', 7), ('''a''', 1), ('''a''', 4)]&lt;br /&gt;
# После устойчивой сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''a''', 1), ('''a''', 4), ('''b''', 7)]&lt;br /&gt;
# Восстановив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;, получаем [0, 3, 6].&lt;br /&gt;
* '''Слияние суффиксных массивов'''&lt;br /&gt;
Рассмотрим, к примеру, третью итерацию слияния, к этой итерации массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 2, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0, на ней мы сливаем суффиксы, соответствующие позициям 5 и 0. &lt;br /&gt;
# Образуем тройки (S[5], S[6], S[7..8]) и (S[0], S[1], S[2..8]).&lt;br /&gt;
# После получения относительного порядка суффиксов, получим тройки ('''a''', '''b''', 1) и ('''a''', '''b''', 3). Первая тройка меньше второй, поэтому добавляем суффикс, соответствующий позиции 5 в массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# В конце итерации получаем &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 3, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0.&lt;br /&gt;
К концу слияния получим &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5, 0, 3, 6, 2, 1, 4]. Так как мы добавляли один символ '''$''' в начале алгоритма для дополнения строки до длины, кратной трем, выбросим последний суффикс из &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;, получим в итоге, что &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [7, 5, 0, 3, 6, 2, 1, 4].&lt;br /&gt;
 8 '''$''' (будет выброшен, так как в начале алгоритма добавляли один '''$''' к строке)&lt;br /&gt;
 7 '''$$'''&lt;br /&gt;
 5 '''ab$$'''&lt;br /&gt;
 0 '''abbacab$$'''&lt;br /&gt;
 3 '''acab$$'''&lt;br /&gt;
 6 '''b$$'''&lt;br /&gt;
 2 '''bacab$$'''&lt;br /&gt;
 1 '''bbacab$$'''&lt;br /&gt;
 4 '''cab$$'''&lt;br /&gt;
{|&lt;br /&gt;
| [[Файл:Kark_sanders_stage1.png|325px|thumb| Фаза 1]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage2.png|325px|thumb| Фаза 2]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage3.png|325px|thumb| Фаза 3]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Получение массива LCP ==&lt;br /&gt;
Массив LCP можно получить за линейное время [[Алгоритм_Касаи_и_др. | алгоритмом Касаи]].&lt;br /&gt;
&lt;br /&gt;
== Обобщение алгоритма ==&lt;br /&gt;
На самом деле, алгоритм можно обобщить&amp;lt;ref name=&amp;quot;generalisation&amp;quot;&amp;gt; Juha Kärkkäinen, Peter Sanders and Stefan Burkhardt. Linear work suffix array construction. http://www.cs.helsinki.fi/juha.karkkainen/publications/jacm05-revised.pdf &amp;lt;/ref&amp;gt;, взяв на первом шаге, к примеру, суффиксы, позиции которых по модулю 7 дают 3, 5 и 6. Для этого потребуются некоторое усложнение алгоритма, например, сортировка оставшихся суффиксов в нескольких группах на шаге 2 и слияние нескольких групп на шаге 3, но основная идея алгоритма остается той же. Множества, которые можно выбрать, на первом шаге определяются '''разностным покрытием''' (''difference cover'').&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Разностное покрытие''' (''difference cover'') &amp;lt;tex&amp;gt; D &amp;lt;/tex&amp;gt; по модулю &amp;lt;tex&amp;gt;m &amp;lt;/tex&amp;gt; — множество чисел от &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;m - 1 &amp;lt;/tex&amp;gt; таких, что &amp;lt;tex&amp;gt; \forall i \in [0, m-1]: \exists j, k \in D: i \equiv k - j \pmod m &amp;lt;/tex&amp;gt;.&lt;br /&gt;
}} &lt;br /&gt;
Например, &amp;lt;tex&amp;gt; \{1, 2\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt; \{3, 5, 6\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt; \{1\} &amp;lt;/tex&amp;gt; — не является разностным покрытием по модулю &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;, поэтому этот алгоритм не применим к нему. Подробнее узнать, как вычислять разностное покрытие для заданного модуля можно также здесь&amp;lt;ref name=&amp;quot;generalisation&amp;quot;/&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Источники информации==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
* [http://www.cs.helsinki.fi/juha.karkkainen/publications/icalp03.pdf Juha Kärkkäinen and Peter Sanders. Simple linear work suffix array construction]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Суффиксный массив]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=47724</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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=47724"/>
				<updated>2015-06-06T09:38:01Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Алгоритм «разделяй и властвуй» */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Алгоритм Карккайнена-Сандерса''' (Kärkkäinen, Sanders) — алгоритм построения [[суффиксный массив | суффиксного массива]] за линейное время.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Используемые обозначения ==&lt;br /&gt;
* В данном конспекте используется 0-индексация.&lt;br /&gt;
* &amp;lt;tex&amp;gt;S[i..j] &amp;lt;/tex&amp;gt; — подстрока строки &amp;lt;tex&amp;gt; S &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;-й символы включительно.&lt;br /&gt;
* Пусть длина строки &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;. Обозначим &amp;lt;tex&amp;gt; S[i..j] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; j \geqslant n &amp;lt;/tex&amp;gt; как строку &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt;, дополненную защитными символами &amp;lt;tex&amp;gt; \$ &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;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Четным суффиксом''' назовем суффикс, начинающийся в четной позиции. &amp;lt;br&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;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left( \dfrac{n}{2}\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого является &amp;lt;tex&amp;gt; T(n) = O(n) &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;
&lt;br /&gt;
* '''Суффиксный массив для нечетных суффиксов'''&lt;br /&gt;
&lt;br /&gt;
# Отобразим исходную строку &amp;lt;tex&amp;gt; S &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; длины  &amp;lt;tex&amp;gt; \dfrac{n}{2} &amp;lt;/tex&amp;gt;  следующим образом:&lt;br /&gt;
#* Сделаем список, состоящий из пар символов вида &amp;lt;tex&amp;gt; S[i..i + 1] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; i \bmod 2 = 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его цифровой сортировкой за линейное время и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S[1..n] &amp;lt;/tex&amp;gt; в алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;, получив строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; половинной длины.&lt;br /&gt;
# Рекурсивно построим суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Построим суффиксный массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;. Очевидно, &amp;lt;tex&amp;gt; A_{S_o}[i] = 2 A_{S'}[i] + 1 &amp;lt;/tex&amp;gt;, так отношение упорядоченности любых двух строк в старом алфавите &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt; эквивалентно отношению упорядоченности в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; по его построению.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для четных суффиксов'''&lt;br /&gt;
На этом шаге мы за линейное время получим суффиксный массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; для четных суффиксов, используя уже построенный &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что сортировка множества четных суффиксов &amp;lt;tex&amp;gt; \{ S[i..n] | i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке множества пар &amp;lt;tex&amp;gt; \{ (S[i], S[i+1..n]) | i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt;. Однако &amp;lt;tex&amp;gt; S[i+1..n] &amp;lt;/tex&amp;gt; — нечетный суффикс, и его относительную позицию мы уже узнали на шаге 1.&lt;br /&gt;
&lt;br /&gt;
Таким образом, чтобы отсортировать эти пары за линейное время, сначала сразу выпишем их в порядке возрастания второго элемента пары (то есть в порядке вхождения в массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;), а потом отсортируем устойчивой сортировкой подсчетом по первым элементам. После этого легко можно восстановить массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
     M.add(Pair(S[&amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
    &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&gt;
&lt;br /&gt;
Заметим, что массив &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; перед сортировкой подсчетом не был явно отсортирован по вторым элементам, и хранил не суффиксы, а их позиции в строке &amp;lt;tex&amp;gt; S &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;
Для суффиксного дерева третий шаг алгоритма опирается на специфические особенности суффиксных деревьев, которые не присущи суффиксным массивам&amp;lt;ref&amp;gt;M. Farach. Optimal suffix tree construction with large alphabets. http://www.cs.rutgers.edu/~farach/pubs/FarFerrMuthu00.pdf &amp;lt;/ref&amp;gt; .  &lt;br /&gt;
В случае суффиксного массива слияние становится очень сложным, но все же оно было реализовано в алгоритме Ким-Сим-Парк-Парка&amp;lt;ref&amp;gt; D. K. Kim, J. S. Sim, H. Park, and K. Park. Linear-time construction of suffix arrays. http://www.springerlink.com/content/568156021q45r320/&amp;lt;/ref&amp;gt;. Однако простой модификацией алгоритма можно значительно упростить его.&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
Покажем первые два шага агоритма для строки '''ababbbaa'''.&lt;br /&gt;
&lt;br /&gt;
Во-первых, добавив защитный символ '''$''', получив строку '''ababbbaa$''' (для этого алгоритма он не требуется, но может понадобиться в применениях суффиксного массива). Во-вторых, дополним ее до четной длины, получив '''ababbbaa$$'''.&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для нечетных суффиксов'''&lt;br /&gt;
# В новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет четыре элемента — '''ba''', '''bb''', '''a$''', '''$$'''. После сортировки они получат номера 2, 3, 1 и 0 соответственно.&lt;br /&gt;
# Переводим строку S[1..n] = '''babbbaa$$$''' в новый алфавит. Сжатой строкой &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; будет '''23210'''.&lt;br /&gt;
# После рекурсивного вызова получим, что &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [4, 3, 2, 0, 1], и &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; = [9, 7, 5, 1, 3].&lt;br /&gt;
&lt;br /&gt;
* '''Суффиксный массив для четных суффиксов'''&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''$''', 9), ('''a''', 7), ('''b''', 5), ('''a''', 1), ('''a''', 3)].&lt;br /&gt;
# После сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt;= [('''$''', 9), ('''a''', 7), ('''a''', 1), ('''a''', 3), ('''b''', 5)].&lt;br /&gt;
# Восстановив массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;, получаем [8, 6, 0, 2, 4], что действительно является суффиксным массивом для четных суффиксов.&lt;br /&gt;
&lt;br /&gt;
* '''Слияние суффиксных массивов'''&lt;br /&gt;
Если бы мы умели сливать &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; за линейное время, получили бы:&lt;br /&gt;
 9 '''$'''&lt;br /&gt;
 8 '''$$'''&lt;br /&gt;
 7 '''a$$'''&lt;br /&gt;
 6 '''aa$$'''&lt;br /&gt;
 0 '''ababbbaa$$'''&lt;br /&gt;
 2 '''abbbaa$$'''&lt;br /&gt;
 5 '''baa$$'''&lt;br /&gt;
 1 '''babbbaa$$'''&lt;br /&gt;
 4 '''bbaa$$'''&lt;br /&gt;
 3 '''bbbaa$$'''&lt;br /&gt;
&lt;br /&gt;
Как и было сказано вначале, избавиться от лишних '''$''' легко, так как суффиксы, им соответствующие, будут первыми в суффиксном массиве (в данном случае достаточно выбросить &amp;quot;9&amp;quot; из суффиксного массива).&lt;br /&gt;
&lt;br /&gt;
== Алгоритм Карккайнена-Сандерса == &lt;br /&gt;
Изменим изначальный алгоритм следующим образом:&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих позициям, не кратным трем. Рекурсивно сведем это к построению суффиксного массива для строки длиной в две трети исходной.&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих кратным трем позициям, используя результат первого шага, за линейное время.&lt;br /&gt;
# Сольем эти суффиксные массивы в один за линейное время.&lt;br /&gt;
&lt;br /&gt;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left(\dfrac23 n\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого также является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt; (это видно из того, что сумма геометрической прогрессии с основанием &amp;lt;tex&amp;gt; \dfrac23 &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Аналогично первой версии алгоритма, дополним строку &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; до длины, кратной трем, защитными символами &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* '''База рекурсии'''&lt;br /&gt;
Для этого алгоритма минимальной базой рекурсии будет строка длиной 4, так как она дополняется до длины 6, после чего вновь следует рекурсивный вызов строки длиной 4, и, если бы база была меньше 4, алгоритм вошел бы в бесконечную рекурсию. На этом этапе также можно применить обычную сортировку суффиксов, так как это потребует &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
* '''Шаг 1'''&lt;br /&gt;
На этом шаге строится суффиксный массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; для множества суффиксов &amp;lt;tex&amp;gt; \{ S[i..n-1] | i \bmod 3 \ne 0 \} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Получим строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; аналогично предыдущему алгоритму:&lt;br /&gt;
#* Сделаем список, состоящий из троек &amp;lt;tex&amp;gt; S[i..i+2]&amp;lt;/tex&amp;gt; , где &amp;lt;tex&amp;gt; i \bmod 3 \ne 0 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его за линейное время цифровой сортировкой и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S[1..n]S[2..n+1] &amp;lt;/tex&amp;gt; в строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; длиной &amp;lt;tex&amp;gt; \dfrac23 n &amp;lt;/tex&amp;gt; в алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;. Тогда суффиксу &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt; в старом алфавите, где &amp;lt;tex&amp;gt; i \bmod 3 = 1 &amp;lt;/tex&amp;gt;, в новом алфавите будет соответствовать строка &amp;lt;tex&amp;gt; S'\left[\dfrac{i-1}{3}..\dfrac{n}{3} - 1\right] &amp;lt;/tex&amp;gt;, а если &amp;lt;tex&amp;gt; i\bmod 3 = 2 &amp;lt;/tex&amp;gt;, то строка &amp;lt;tex&amp;gt; S'\left[\dfrac{n}{3} + \dfrac{i-2}{3}..\dfrac{2n}{3} - 1\right] &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Вызовем алгоритм рекурсивно для строки &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt;, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Пройдем по массиву &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt; A_{S'}[i] &amp;lt; \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j = 3A_{S'}[i] + 1 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;, если же &amp;lt;tex&amp;gt; A_{S'}[i] \geqslant \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j =  3\left(A_{S'}[i] - \dfrac{n}{3}\right) + 2 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;. Псевдокод получения &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;.length - 1:&lt;br /&gt;
    '''if''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] &amp;lt; n/3:&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] + 1)&lt;br /&gt;
    '''else''':&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * (&amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] - n/3) + 2)&lt;br /&gt;
&lt;br /&gt;
* '''Шаг 2'''&lt;br /&gt;
Этот шаг также аналогичен первой версии алгоритма. Сортировка множества &amp;lt;tex&amp;gt; \{ S[i..n-1] | i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке пар &amp;lt;tex&amp;gt; \{ (S[i], S[i+1..n-1]) | i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; S[i+1..n-1] &amp;lt;/tex&amp;gt; — суффиксы в позициях, равных 1 по модулю 3, относительный порядок которых уже известен. Выпишем эти пары в порядке вхождения их в &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и отсортируем по первому элементу устойчивой сортировкой подсчетом, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;. Псевдокод этого шага:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' 2n/3 - 1:&lt;br /&gt;
     '''if''' &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[i] % 3 == 1:&lt;br /&gt;
         M.add(Pair(S[&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 '''for''' i = 0 '''to''' n/3 - 1:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&gt;
Аналогично, второй шаг требует &amp;lt;tex&amp;gt; O(n) &amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
* '''Шаг 3'''&lt;br /&gt;
На этом шаге мы должны слить суффиксные массивы &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, чтобы получить суффиксный массив &amp;lt;tex&amp;gt; A_{S} &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;
&lt;br /&gt;
Пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 1 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Это аналогично сравнению пар &amp;lt;tex&amp;gt; (S[i], S[i+1..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S[j], S[j+1..n-1]) &amp;lt;/tex&amp;gt;. Сравнить первые элементы пар мы можем за &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt;, а относительный порядок вторых элементов пар нам уже известен, так как они соответствуют позициям, равным 2 и 1 по модулю 3 соответственно.&lt;br /&gt;
&lt;br /&gt;
Аналогично, пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 2 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Тогда это аналогично сравнению троек &amp;lt;tex&amp;gt; (S[i], S[i+1], S[i+2..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S[j], S[j+1], S[j+2..n-1]) &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;A_{S}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 // Вначале предподсчитаем за O(n) обратную перестановку для суффиксного массива &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;, то есть массив rank такой, что &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[rank[i]] = i.&lt;br /&gt;
 // Тогда мы сможем за O(1) сравнивать суффиксы по их позиции.&lt;br /&gt;
 rank = inverse(&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;) &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3 '''and''' j &amp;lt; n/3:&lt;br /&gt;
     pos12 = &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i]&lt;br /&gt;
     pos0  = &amp;lt;tex&amp;gt; A_{0} &amp;lt;/tex&amp;gt;[j]&lt;br /&gt;
     '''if''' pos12 % 3 == 1:&lt;br /&gt;
        ''' if''' Pair(S[pos12], rank[pos12 + 1]) &amp;lt; Pair(S[pos0], rank[pos0 + 1]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++  &lt;br /&gt;
     '''else''':&lt;br /&gt;
         '''if''' Triple(S[pos12], S[pos12 + 1], rank[pos12 + 2]) &amp;lt; Triple(S[pos0], S[pos0 + 1], rank[pos0 + 2]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++ &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i])&lt;br /&gt;
     i++&lt;br /&gt;
 '''while''' j &amp;lt; n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{0}} &amp;lt;/tex&amp;gt;[j])&lt;br /&gt;
     j++&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;
Построим суффиксный массив для строки '''abbacab'''. После добавления защитного символа и дополнения до кратной трем длины, получим '''abbacab$$'''.&lt;br /&gt;
* '''Шаг 1'''&lt;br /&gt;
# Тройками, соответствующими равными 1 по модулю 3 позициям, будут: '''bba''', '''cab''', '''$$$''', соответствующими равным 2 по модулю 3 — '''bac''', '''ab$''', '''$$$'''. Новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет содержать элементы '''bba''', '''cab''', '''$$$''', '''bac''', '''ab$''', которые после сортировки получат номера 3, 4, 0, 2, 1 соотвествтенно.&lt;br /&gt;
# Строкой '''bbacab$$$bacab$$$$''' в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; = '''340210'''. &lt;br /&gt;
# После рекурсивного вызова получим &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [5, 2, 4, 3, 0, 1]. Пересчитав &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, получим [(5 - 3)*3 + 2, 2 * 3 + 1, (4 - 3) * 3 + 2, (3 - 3) * 3 + 2, 0 * 3 + 1, 1 * 3 + 1] = [8, 7, 5, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
* '''Шаг 2'''&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и выбрав в нем элементы, равные 1 по модулю 3, получим массив пар &amp;lt;tex&amp;gt;M&amp;lt;/tex&amp;gt; = [('''b''', 7), ('''a''', 1), ('''a''', 4)]&lt;br /&gt;
# После устойчивой сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''a''', 1), ('''a''', 4), ('''b''', 7)]&lt;br /&gt;
# Восстановив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;, получаем [0, 3, 6].&lt;br /&gt;
* '''Шаг 3'''&lt;br /&gt;
Рассмотрим, к примеру, третью итерацию слияния, к этой итерации массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 2, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0, на ней мы сливаем суффиксы, соответствующие позициям 5 и 0. &lt;br /&gt;
# Образуем тройки (S[5], S[6], S[7..8]) и (S[0], S[1], S[2..8]).&lt;br /&gt;
# После получения относительного порядка суффиксов, получим тройки ('''a''', '''b''', 1) и ('''a''', '''b''', 3). Первая тройка меньше второй, поэтому добавляем суффикс, соответствующий позиции 5 в массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# В конце итерации получаем &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 3, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0.&lt;br /&gt;
К концу слияния получим &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5, 0, 3, 6, 2, 1, 4]. Так как мы добавляли один символ '''$''' в начале алгоритма для дополнения строки до длины, кратной трем, выбросим последний суффикс из &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;, получим в итоге, что &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [7, 5, 0, 3, 6, 2, 1, 4].&lt;br /&gt;
 8 '''$''' (будет выброшен, так как в начале алгоритма добавляли один '''$''' к строке)&lt;br /&gt;
 7 '''$$'''&lt;br /&gt;
 5 '''ab$$'''&lt;br /&gt;
 0 '''abbacab$$'''&lt;br /&gt;
 3 '''acab$$'''&lt;br /&gt;
 6 '''b$$'''&lt;br /&gt;
 2 '''bacab$$'''&lt;br /&gt;
 1 '''bbacab$$'''&lt;br /&gt;
 4 '''cab$$'''&lt;br /&gt;
{|&lt;br /&gt;
| [[Файл:Kark_sanders_stage1.png|325px|thumb| Фаза 1]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage2.png|325px|thumb| Фаза 2]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage3.png|325px|thumb| Фаза 3]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Получение массива LCP ==&lt;br /&gt;
Массив LCP можно получить за линейное время [[Алгоритм_Касаи_и_др. | алгоритмом Касаи]].&lt;br /&gt;
&lt;br /&gt;
== Обобщение алгоритма ==&lt;br /&gt;
На самом деле, алгоритм можно обобщить&amp;lt;ref name=&amp;quot;generalisation&amp;quot;&amp;gt; Juha Kärkkäinen, Peter Sanders and Stefan Burkhardt. Linear work suffix array construction. http://www.cs.helsinki.fi/juha.karkkainen/publications/jacm05-revised.pdf &amp;lt;/ref&amp;gt;, взяв на первом шаге, к примеру, суффиксы, позиции которых по модулю 7 дают 3, 5 и 6. Для этого потребуются некоторое усложнение алгоритма, например, сортировка оставшихся суффиксов в нескольких группах на шаге 2 и слияние нескольких групп на шаге 3, но основная идея алгоритма остается той же. Множества, которые можно выбрать, на первом шаге определяются '''разностным покрытием''' (''difference cover'').&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Разностное покрытие''' (''difference cover'') &amp;lt;tex&amp;gt; D &amp;lt;/tex&amp;gt; по модулю &amp;lt;tex&amp;gt;m &amp;lt;/tex&amp;gt; — множество чисел от &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;m - 1 &amp;lt;/tex&amp;gt; таких, что &amp;lt;tex&amp;gt; \forall i \in [0, m-1]: \exists j, k \in D: i \equiv k - j \pmod m &amp;lt;/tex&amp;gt;.&lt;br /&gt;
}} &lt;br /&gt;
Например, &amp;lt;tex&amp;gt; \{1, 2\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt; \{3, 5, 6\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt; \{1\} &amp;lt;/tex&amp;gt; — не является разностным покрытием по модулю &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;, поэтому этот алгоритм не применим к нему. Подробнее узнать, как вычислять разностное покрытие для заданного модуля можно также здесь&amp;lt;ref name=&amp;quot;generalisation&amp;quot;/&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Источники информации==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
* [http://www.cs.helsinki.fi/juha.karkkainen/publications/icalp03.pdf Juha Kärkkäinen and Peter Sanders. Simple linear work suffix array construction]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Суффиксный массив]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=47723</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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=47723"/>
				<updated>2015-06-06T08:51:48Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Алгоритм Карккайнена-Сандерса */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Алгоритм Карккайнена-Сандерса''' (Kärkkäinen, Sanders) — алгоритм построения [[суффиксный массив | суффиксного массива]] за линейное время.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Используемые обозначения ==&lt;br /&gt;
* В данном конспекте используется 0-индексация.&lt;br /&gt;
* &amp;lt;tex&amp;gt;S[i..j] &amp;lt;/tex&amp;gt; — подстрока строки &amp;lt;tex&amp;gt; S &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;-й символы включительно.&lt;br /&gt;
* Пусть длина строки &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;. Обозначим &amp;lt;tex&amp;gt; S[i..j] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; j \geqslant n &amp;lt;/tex&amp;gt; как строку &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt;, дополненную защитными символами &amp;lt;tex&amp;gt; \$ &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;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Четным суффиксом''' назовем суффикс, начинающийся в четной позиции. &amp;lt;br&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;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left( \dfrac{n}{2}\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого является &amp;lt;tex&amp;gt; T(n) = O(n) &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;
&lt;br /&gt;
* '''Шаг 1'''&lt;br /&gt;
На первом шаге мы строим суффиксный массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;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; длины &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt; в строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; длины  &amp;lt;tex&amp;gt; \dfrac{n}{2} &amp;lt;/tex&amp;gt;  следующим образом:&lt;br /&gt;
#* Сделаем список, состоящий из пар символов вида &amp;lt;tex&amp;gt; S[i..i + 1] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; i \bmod 2 = 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его цифровой сортировкой за линейное время и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S[1..n] &amp;lt;/tex&amp;gt; в алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;, получив строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; половинной длины.&lt;br /&gt;
# Рекурсивно построим суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Построим суффиксный массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;. Очевидно, &amp;lt;tex&amp;gt; A_{S_o}[i] = 2 A_{S'}[i] + 1 &amp;lt;/tex&amp;gt;, так отношение упорядоченности любых двух строк в старом алфавите &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt; эквивалентно отношению упорядоченности в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; по его построению.&lt;br /&gt;
&lt;br /&gt;
* '''Шаг 2'''&lt;br /&gt;
На этом шаге мы за линейное время получим суффиксный массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; для четных суффиксов, используя уже построенный &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что сортировка множества четных суффиксов &amp;lt;tex&amp;gt; \{ S[i..n] | i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке множества пар &amp;lt;tex&amp;gt; \{ (S[i], S[i+1..n]) | i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt;. Однако &amp;lt;tex&amp;gt; S[i+1..n] &amp;lt;/tex&amp;gt; — нечетный суффикс, и его относительную позицию мы уже узнали на шаге 1.&lt;br /&gt;
&lt;br /&gt;
Таким образом, чтобы отсортировать эти пары за линейное время, сначала сразу выпишем их в порядке возрастания второго элемента пары (то есть в порядке вхождения в массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;), а потом отсортируем устойчивой сортировкой подсчетом по первым элементам. После этого легко можно восстановить массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
     M.add(Pair(S[&amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
    &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&gt;
&lt;br /&gt;
Заметим, что массив &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; перед сортировкой подсчетом не был явно отсортирован по вторым элементам, и хранил не суффиксы, а их позиции в строке &amp;lt;tex&amp;gt; S &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;
* '''Шаг 3'''&lt;br /&gt;
Для суффиксного дерева третий шаг алгоритма опирается на специфические особенности суффиксных деревьев, которые не присущи суффиксным массивам&amp;lt;ref&amp;gt;M. Farach. Optimal suffix tree construction with large alphabets. http://www.cs.rutgers.edu/~farach/pubs/FarFerrMuthu00.pdf &amp;lt;/ref&amp;gt; .  &lt;br /&gt;
В случае суффиксного массива слияние становится очень сложным, но все же оно было реализовано в алгоритме Ким-Сим-Парк-Парка&amp;lt;ref&amp;gt; D. K. Kim, J. S. Sim, H. Park, and K. Park. Linear-time construction of suffix arrays. http://www.springerlink.com/content/568156021q45r320/&amp;lt;/ref&amp;gt;. Однако простой модификацией алгоритма можно значительно упростить его.&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
Покажем первые два шага агоритма для строки '''ababbbaa'''.&lt;br /&gt;
&lt;br /&gt;
Во-первых, добавив защитный символ '''$''', получив строку '''ababbbaa$''' (для этого алгоритма он не требуется, но может понадобиться в применениях суффиксного массива). Во-вторых, дополним ее до четной длины, получив '''ababbbaa$$'''.&lt;br /&gt;
&lt;br /&gt;
* '''Шаг 1'''&lt;br /&gt;
# В новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет четыре элемента — '''ba''', '''bb''', '''a$''', '''$$'''. После сортировки они получат номера 2, 3, 1 и 0 соответственно.&lt;br /&gt;
# Переводим строку S[1..n] = '''babbbaa$$$''' в новый алфавит. Сжатой строкой &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; будет '''23210'''.&lt;br /&gt;
# После рекурсивного вызова получим, что &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [4, 3, 2, 0, 1], и &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; = [9, 7, 5, 1, 3].&lt;br /&gt;
&lt;br /&gt;
* '''Шаг 2'''&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''$''', 9), ('''a''', 7), ('''b''', 5), ('''a''', 1), ('''a''', 3)].&lt;br /&gt;
# После сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt;= [('''$''', 9), ('''a''', 7), ('''a''', 1), ('''a''', 3), ('''b''', 5)].&lt;br /&gt;
# Восстановив массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;, получаем [8, 6, 0, 2, 4], что действительно является суффиксным массивом для четных суффиксов.&lt;br /&gt;
&lt;br /&gt;
* '''Шаг 3'''&lt;br /&gt;
Если бы мы умели сливать &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; за линейное время, получили бы:&lt;br /&gt;
 9 '''$'''&lt;br /&gt;
 8 '''$$'''&lt;br /&gt;
 7 '''a$$'''&lt;br /&gt;
 6 '''aa$$'''&lt;br /&gt;
 0 '''ababbbaa$$'''&lt;br /&gt;
 2 '''abbbaa$$'''&lt;br /&gt;
 5 '''baa$$'''&lt;br /&gt;
 1 '''babbbaa$$'''&lt;br /&gt;
 4 '''bbaa$$'''&lt;br /&gt;
 3 '''bbbaa$$'''&lt;br /&gt;
&lt;br /&gt;
Как и было сказано вначале, избавиться от лишних '''$''' легко, так как суффиксы, им соответствующие, будут первыми в суффиксном массиве (в данном случае достаточно выбросить &amp;quot;9&amp;quot; из суффиксного массива).&lt;br /&gt;
&lt;br /&gt;
== Алгоритм Карккайнена-Сандерса == &lt;br /&gt;
Изменим изначальный алгоритм следующим образом:&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих позициям, не кратным трем. Рекурсивно сведем это к построению суффиксного массива для строки длиной в две трети исходной.&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих кратным трем позициям, используя результат первого шага, за линейное время.&lt;br /&gt;
# Сольем эти суффиксные массивы в один за линейное время.&lt;br /&gt;
&lt;br /&gt;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left(\dfrac23 n\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого также является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt; (это видно из того, что сумма геометрической прогрессии с основанием &amp;lt;tex&amp;gt; \dfrac23 &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Аналогично первой версии алгоритма, дополним строку &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; до длины, кратной трем, защитными символами &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* '''База рекурсии'''&lt;br /&gt;
Для этого алгоритма минимальной базой рекурсии будет строка длиной 4, так как она дополняется до длины 6, после чего вновь следует рекурсивный вызов строки длиной 4, и, если бы база была меньше 4, алгоритм вошел бы в бесконечную рекурсию. На этом этапе также можно применить обычную сортировку суффиксов, так как это потребует &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
* '''Шаг 1'''&lt;br /&gt;
На этом шаге строится суффиксный массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; для множества суффиксов &amp;lt;tex&amp;gt; \{ S[i..n-1] | i \bmod 3 \ne 0 \} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Получим строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; аналогично предыдущему алгоритму:&lt;br /&gt;
#* Сделаем список, состоящий из троек &amp;lt;tex&amp;gt; S[i..i+2]&amp;lt;/tex&amp;gt; , где &amp;lt;tex&amp;gt; i \bmod 3 \ne 0 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его за линейное время цифровой сортировкой и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S[1..n]S[2..n+1] &amp;lt;/tex&amp;gt; в строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; длиной &amp;lt;tex&amp;gt; \dfrac23 n &amp;lt;/tex&amp;gt; в алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;. Тогда суффиксу &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt; в старом алфавите, где &amp;lt;tex&amp;gt; i \bmod 3 = 1 &amp;lt;/tex&amp;gt;, в новом алфавите будет соответствовать строка &amp;lt;tex&amp;gt; S'\left[\dfrac{i-1}{3}..\dfrac{n}{3} - 1\right] &amp;lt;/tex&amp;gt;, а если &amp;lt;tex&amp;gt; i\bmod 3 = 2 &amp;lt;/tex&amp;gt;, то строка &amp;lt;tex&amp;gt; S'\left[\dfrac{n}{3} + \dfrac{i-2}{3}..\dfrac{2n}{3} - 1\right] &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Вызовем алгоритм рекурсивно для строки &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt;, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Пройдем по массиву &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt; A_{S'}[i] &amp;lt; \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j = 3A_{S'}[i] + 1 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;, если же &amp;lt;tex&amp;gt; A_{S'}[i] \geqslant \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j =  3\left(A_{S'}[i] - \dfrac{n}{3}\right) + 2 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;. Псевдокод получения &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;.length - 1:&lt;br /&gt;
    '''if''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] &amp;lt; n/3:&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] + 1)&lt;br /&gt;
    '''else''':&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * (&amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] - n/3) + 2)&lt;br /&gt;
&lt;br /&gt;
* '''Шаг 2'''&lt;br /&gt;
Этот шаг также аналогичен первой версии алгоритма. Сортировка множества &amp;lt;tex&amp;gt; \{ S[i..n-1] | i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке пар &amp;lt;tex&amp;gt; \{ (S[i], S[i+1..n-1]) | i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; S[i+1..n-1] &amp;lt;/tex&amp;gt; — суффиксы в позициях, равных 1 по модулю 3, относительный порядок которых уже известен. Выпишем эти пары в порядке вхождения их в &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и отсортируем по первому элементу устойчивой сортировкой подсчетом, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;. Псевдокод этого шага:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' 2n/3 - 1:&lt;br /&gt;
     '''if''' &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[i] % 3 == 1:&lt;br /&gt;
         M.add(Pair(S[&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 '''for''' i = 0 '''to''' n/3 - 1:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&gt;
Аналогично, второй шаг требует &amp;lt;tex&amp;gt; O(n) &amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
* '''Шаг 3'''&lt;br /&gt;
На этом шаге мы должны слить суффиксные массивы &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, чтобы получить суффиксный массив &amp;lt;tex&amp;gt; A_{S} &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;
&lt;br /&gt;
Пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 1 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Это аналогично сравнению пар &amp;lt;tex&amp;gt; (S[i], S[i+1..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S[j], S[j+1..n-1]) &amp;lt;/tex&amp;gt;. Сравнить первые элементы пар мы можем за &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt;, а относительный порядок вторых элементов пар нам уже известен, так как они соответствуют позициям, равным 2 и 1 по модулю 3 соответственно.&lt;br /&gt;
&lt;br /&gt;
Аналогично, пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 2 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Тогда это аналогично сравнению троек &amp;lt;tex&amp;gt; (S[i], S[i+1], S[i+2..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S[j], S[j+1], S[j+2..n-1]) &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;A_{S}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 // Вначале предподсчитаем за O(n) обратную перестановку для суффиксного массива &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;, то есть массив rank такой, что &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[rank[i]] = i.&lt;br /&gt;
 // Тогда мы сможем за O(1) сравнивать суффиксы по их позиции.&lt;br /&gt;
 rank = inverse(&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;) &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3 '''and''' j &amp;lt; n/3:&lt;br /&gt;
     pos12 = &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i]&lt;br /&gt;
     pos0  = &amp;lt;tex&amp;gt; A_{0} &amp;lt;/tex&amp;gt;[j]&lt;br /&gt;
     '''if''' pos12 % 3 == 1:&lt;br /&gt;
        ''' if''' Pair(S[pos12], rank[pos12 + 1]) &amp;lt; Pair(S[pos0], rank[pos0 + 1]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++  &lt;br /&gt;
     '''else''':&lt;br /&gt;
         '''if''' Triple(S[pos12], S[pos12 + 1], rank[pos12 + 2]) &amp;lt; Triple(S[pos0], S[pos0 + 1], rank[pos0 + 2]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++ &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i])&lt;br /&gt;
     i++&lt;br /&gt;
 '''while''' j &amp;lt; n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{0}} &amp;lt;/tex&amp;gt;[j])&lt;br /&gt;
     j++&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;
Построим суффиксный массив для строки '''abbacab'''. После добавления защитного символа и дополнения до кратной трем длины, получим '''abbacab$$'''.&lt;br /&gt;
* '''Шаг 1'''&lt;br /&gt;
# Тройками, соответствующими равными 1 по модулю 3 позициям, будут: '''bba''', '''cab''', '''$$$''', соответствующими равным 2 по модулю 3 — '''bac''', '''ab$''', '''$$$'''. Новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет содержать элементы '''bba''', '''cab''', '''$$$''', '''bac''', '''ab$''', которые после сортировки получат номера 3, 4, 0, 2, 1 соотвествтенно.&lt;br /&gt;
# Строкой '''bbacab$$$bacab$$$$''' в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; = '''340210'''. &lt;br /&gt;
# После рекурсивного вызова получим &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [5, 2, 4, 3, 0, 1]. Пересчитав &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, получим [(5 - 3)*3 + 2, 2 * 3 + 1, (4 - 3) * 3 + 2, (3 - 3) * 3 + 2, 0 * 3 + 1, 1 * 3 + 1] = [8, 7, 5, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
* '''Шаг 2'''&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и выбрав в нем элементы, равные 1 по модулю 3, получим массив пар &amp;lt;tex&amp;gt;M&amp;lt;/tex&amp;gt; = [('''b''', 7), ('''a''', 1), ('''a''', 4)]&lt;br /&gt;
# После устойчивой сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''a''', 1), ('''a''', 4), ('''b''', 7)]&lt;br /&gt;
# Восстановив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;, получаем [0, 3, 6].&lt;br /&gt;
* '''Шаг 3'''&lt;br /&gt;
Рассмотрим, к примеру, третью итерацию слияния, к этой итерации массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 2, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0, на ней мы сливаем суффиксы, соответствующие позициям 5 и 0. &lt;br /&gt;
# Образуем тройки (S[5], S[6], S[7..8]) и (S[0], S[1], S[2..8]).&lt;br /&gt;
# После получения относительного порядка суффиксов, получим тройки ('''a''', '''b''', 1) и ('''a''', '''b''', 3). Первая тройка меньше второй, поэтому добавляем суффикс, соответствующий позиции 5 в массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# В конце итерации получаем &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 3, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0.&lt;br /&gt;
К концу слияния получим &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5, 0, 3, 6, 2, 1, 4]. Так как мы добавляли один символ '''$''' в начале алгоритма для дополнения строки до длины, кратной трем, выбросим последний суффикс из &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;, получим в итоге, что &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [7, 5, 0, 3, 6, 2, 1, 4].&lt;br /&gt;
 8 '''$''' (будет выброшен, так как в начале алгоритма добавляли один '''$''' к строке)&lt;br /&gt;
 7 '''$$'''&lt;br /&gt;
 5 '''ab$$'''&lt;br /&gt;
 0 '''abbacab$$'''&lt;br /&gt;
 3 '''acab$$'''&lt;br /&gt;
 6 '''b$$'''&lt;br /&gt;
 2 '''bacab$$'''&lt;br /&gt;
 1 '''bbacab$$'''&lt;br /&gt;
 4 '''cab$$'''&lt;br /&gt;
{|&lt;br /&gt;
| [[Файл:Kark_sanders_stage1.png|325px|thumb| Фаза 1]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage2.png|325px|thumb| Фаза 2]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage3.png|325px|thumb| Фаза 3]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Получение массива LCP ==&lt;br /&gt;
Массив LCP можно получить за линейное время [[Алгоритм_Касаи_и_др. | алгоритмом Касаи]].&lt;br /&gt;
&lt;br /&gt;
== Обобщение алгоритма ==&lt;br /&gt;
На самом деле, алгоритм можно обобщить&amp;lt;ref name=&amp;quot;generalisation&amp;quot;&amp;gt; Juha Kärkkäinen, Peter Sanders and Stefan Burkhardt. Linear work suffix array construction. http://www.cs.helsinki.fi/juha.karkkainen/publications/jacm05-revised.pdf &amp;lt;/ref&amp;gt;, взяв на первом шаге, к примеру, суффиксы, позиции которых по модулю 7 дают 3, 5 и 6. Для этого потребуются некоторое усложнение алгоритма, например, сортировка оставшихся суффиксов в нескольких группах на шаге 2 и слияние нескольких групп на шаге 3, но основная идея алгоритма остается той же. Множества, которые можно выбрать, на первом шаге определяются '''разностным покрытием''' (''difference cover'').&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Разностное покрытие''' (''difference cover'') &amp;lt;tex&amp;gt; D &amp;lt;/tex&amp;gt; по модулю &amp;lt;tex&amp;gt;m &amp;lt;/tex&amp;gt; — множество чисел от &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;m - 1 &amp;lt;/tex&amp;gt; таких, что &amp;lt;tex&amp;gt; \forall i \in [0, m-1]: \exists j, k \in D: i \equiv k - j \pmod m &amp;lt;/tex&amp;gt;.&lt;br /&gt;
}} &lt;br /&gt;
Например, &amp;lt;tex&amp;gt; \{1, 2\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt; \{3, 5, 6\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt; \{1\} &amp;lt;/tex&amp;gt; — не является разностным покрытием по модулю &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;, поэтому этот алгоритм не применим к нему. Подробнее узнать, как вычислять разностное покрытие для заданного модуля можно также здесь&amp;lt;ref name=&amp;quot;generalisation&amp;quot;/&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Источники информации==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
* [http://www.cs.helsinki.fi/juha.karkkainen/publications/icalp03.pdf Juha Kärkkäinen and Peter Sanders. Simple linear work suffix array construction]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Суффиксный массив]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=47722</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%9A%D0%B0%D1%80%D0%BA%D0%BA%D0%B0%D0%B9%D0%BD%D0%B5%D0%BD%D0%B0-%D0%A1%D0%B0%D0%BD%D0%B4%D0%B5%D1%80%D1%81%D0%B0&amp;diff=47722"/>
				<updated>2015-06-06T08:49:52Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Алгоритм «разделяй и властвуй» */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Алгоритм Карккайнена-Сандерса''' (Kärkkäinen, Sanders) — алгоритм построения [[суффиксный массив | суффиксного массива]] за линейное время.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Используемые обозначения ==&lt;br /&gt;
* В данном конспекте используется 0-индексация.&lt;br /&gt;
* &amp;lt;tex&amp;gt;S[i..j] &amp;lt;/tex&amp;gt; — подстрока строки &amp;lt;tex&amp;gt; S &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;-й символы включительно.&lt;br /&gt;
* Пусть длина строки &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt;. Обозначим &amp;lt;tex&amp;gt; S[i..j] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; j \geqslant n &amp;lt;/tex&amp;gt; как строку &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt;, дополненную защитными символами &amp;lt;tex&amp;gt; \$ &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;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Четным суффиксом''' назовем суффикс, начинающийся в четной позиции. &amp;lt;br&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;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left( \dfrac{n}{2}\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого является &amp;lt;tex&amp;gt; T(n) = O(n) &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;
&lt;br /&gt;
* '''Шаг 1'''&lt;br /&gt;
На первом шаге мы строим суффиксный массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;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; длины &amp;lt;tex&amp;gt; n &amp;lt;/tex&amp;gt; в строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; длины  &amp;lt;tex&amp;gt; \dfrac{n}{2} &amp;lt;/tex&amp;gt;  следующим образом:&lt;br /&gt;
#* Сделаем список, состоящий из пар символов вида &amp;lt;tex&amp;gt; S[i..i + 1] &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; i \bmod 2 = 1 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его цифровой сортировкой за линейное время и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S[1..n] &amp;lt;/tex&amp;gt; в алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;, получив строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; половинной длины.&lt;br /&gt;
# Рекурсивно построим суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Построим суффиксный массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;. Очевидно, &amp;lt;tex&amp;gt; A_{S_o}[i] = 2 A_{S'}[i] + 1 &amp;lt;/tex&amp;gt;, так отношение упорядоченности любых двух строк в старом алфавите &amp;lt;tex&amp;gt; \Sigma &amp;lt;/tex&amp;gt; эквивалентно отношению упорядоченности в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; по его построению.&lt;br /&gt;
&lt;br /&gt;
* '''Шаг 2'''&lt;br /&gt;
На этом шаге мы за линейное время получим суффиксный массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; для четных суффиксов, используя уже построенный &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что сортировка множества четных суффиксов &amp;lt;tex&amp;gt; \{ S[i..n] | i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке множества пар &amp;lt;tex&amp;gt; \{ (S[i], S[i+1..n]) | i \bmod 2 = 0 \} &amp;lt;/tex&amp;gt;. Однако &amp;lt;tex&amp;gt; S[i+1..n] &amp;lt;/tex&amp;gt; — нечетный суффикс, и его относительную позицию мы уже узнали на шаге 1.&lt;br /&gt;
&lt;br /&gt;
Таким образом, чтобы отсортировать эти пары за линейное время, сначала сразу выпишем их в порядке возрастания второго элемента пары (то есть в порядке вхождения в массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;), а потом отсортируем устойчивой сортировкой подсчетом по первым элементам. После этого легко можно восстановить массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
     M.add(Pair(S[&amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt; A_{S_o}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' n/2 - 1:&lt;br /&gt;
    &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&gt;
&lt;br /&gt;
Заметим, что массив &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; перед сортировкой подсчетом не был явно отсортирован по вторым элементам, и хранил не суффиксы, а их позиции в строке &amp;lt;tex&amp;gt; S &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;
* '''Шаг 3'''&lt;br /&gt;
Для суффиксного дерева третий шаг алгоритма опирается на специфические особенности суффиксных деревьев, которые не присущи суффиксным массивам&amp;lt;ref&amp;gt;M. Farach. Optimal suffix tree construction with large alphabets. http://www.cs.rutgers.edu/~farach/pubs/FarFerrMuthu00.pdf &amp;lt;/ref&amp;gt; .  &lt;br /&gt;
В случае суффиксного массива слияние становится очень сложным, но все же оно было реализовано в алгоритме Ким-Сим-Парк-Парка&amp;lt;ref&amp;gt; D. K. Kim, J. S. Sim, H. Park, and K. Park. Linear-time construction of suffix arrays. http://www.springerlink.com/content/568156021q45r320/&amp;lt;/ref&amp;gt;. Однако простой модификацией алгоритма можно значительно упростить его.&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
Покажем первые два шага агоритма для строки '''ababbbaa'''.&lt;br /&gt;
&lt;br /&gt;
Во-первых, добавив защитный символ '''$''', получив строку '''ababbbaa$''' (для этого алгоритма он не требуется, но может понадобиться в применениях суффиксного массива). Во-вторых, дополним ее до четной длины, получив '''ababbbaa$$'''.&lt;br /&gt;
&lt;br /&gt;
* '''Шаг 1'''&lt;br /&gt;
# В новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет четыре элемента — '''ba''', '''bb''', '''a$''', '''$$'''. После сортировки они получат номера 2, 3, 1 и 0 соответственно.&lt;br /&gt;
# Переводим строку S[1..n] = '''babbbaa$$$''' в новый алфавит. Сжатой строкой &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; будет '''23210'''.&lt;br /&gt;
# После рекурсивного вызова получим, что &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [4, 3, 2, 0, 1], и &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; = [9, 7, 5, 1, 3].&lt;br /&gt;
&lt;br /&gt;
* '''Шаг 2'''&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt;, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''$''', 9), ('''a''', 7), ('''b''', 5), ('''a''', 1), ('''a''', 3)].&lt;br /&gt;
# После сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt;= [('''$''', 9), ('''a''', 7), ('''a''', 1), ('''a''', 3), ('''b''', 5)].&lt;br /&gt;
# Восстановив массив &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt;, получаем [8, 6, 0, 2, 4], что действительно является суффиксным массивом для четных суффиксов.&lt;br /&gt;
&lt;br /&gt;
* '''Шаг 3'''&lt;br /&gt;
Если бы мы умели сливать &amp;lt;tex&amp;gt; A_{S_o} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_e} &amp;lt;/tex&amp;gt; за линейное время, получили бы:&lt;br /&gt;
 9 '''$'''&lt;br /&gt;
 8 '''$$'''&lt;br /&gt;
 7 '''a$$'''&lt;br /&gt;
 6 '''aa$$'''&lt;br /&gt;
 0 '''ababbbaa$$'''&lt;br /&gt;
 2 '''abbbaa$$'''&lt;br /&gt;
 5 '''baa$$'''&lt;br /&gt;
 1 '''babbbaa$$'''&lt;br /&gt;
 4 '''bbaa$$'''&lt;br /&gt;
 3 '''bbbaa$$'''&lt;br /&gt;
&lt;br /&gt;
Как и было сказано вначале, избавиться от лишних '''$''' легко, так как суффиксы, им соответствующие, будут первыми в суффиксном массиве (в данном случае достаточно выбросить &amp;quot;9&amp;quot; из суффиксного массива).&lt;br /&gt;
&lt;br /&gt;
== Алгоритм Карккайнена-Сандерса == &lt;br /&gt;
Изменим изначальный алгоритм следующим образом:&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих позициям, не кратным трем. Рекурсивно сведем это к построению суффиксного массива для строки длиной в две трети исходной.&lt;br /&gt;
# Построим суффиксный массив для суффиксов, соответствующих кратным трем позициям, используя результат первого шага, за линейное время.&lt;br /&gt;
# Сольем эти суффиксные массивы в один за линейное время.&lt;br /&gt;
&lt;br /&gt;
Получили асимптотическое уравнение &amp;lt;tex&amp;gt; T(n) = T\left(\dfrac23 n\right) + O(n) &amp;lt;/tex&amp;gt;, решением которого также является &amp;lt;tex&amp;gt; T(n) = O(n) &amp;lt;/tex&amp;gt; (это видно из того, что сумма геометрической прогрессии с основанием &amp;lt;tex&amp;gt; \dfrac23 &amp;lt;/tex&amp;gt; равна &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Аналогично первой версии алгоритма, дополним строку &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt; до длины, кратной трем, защитными символами &amp;lt;tex&amp;gt; \$ &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== База рекурсии ===&lt;br /&gt;
Для этого алгоритма минимальной базой рекурсии будет строка длиной 4, так как она дополняется до длины 6, после чего вновь следует рекурсивный вызов строки длиной 4, и, если бы база была меньше 4, алгоритм вошел бы в бесконечную рекурсию. На этом этапе также можно применить обычную сортировку суффиксов, так как это потребует &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt; действий.&lt;br /&gt;
&lt;br /&gt;
=== Шаг 1 ===&lt;br /&gt;
На этом шаге строится суффиксный массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; для множества суффиксов &amp;lt;tex&amp;gt; \{ S[i..n-1] | i \bmod 3 \ne 0 \} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Получим строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; аналогично предыдущему алгоритму:&lt;br /&gt;
#* Сделаем список, состоящий из троек &amp;lt;tex&amp;gt; S[i..i+2]&amp;lt;/tex&amp;gt; , где &amp;lt;tex&amp;gt; i \bmod 3 \ne 0 &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Отсортируем его за линейное время цифровой сортировкой и получим новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;.&lt;br /&gt;
#* Перекодируем строку &amp;lt;tex&amp;gt; S[1..n]S[2..n+1] &amp;lt;/tex&amp;gt; в строку &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; длиной &amp;lt;tex&amp;gt; \dfrac23 n &amp;lt;/tex&amp;gt; в алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt;. Тогда суффиксу &amp;lt;tex&amp;gt; S[i..n-1] &amp;lt;/tex&amp;gt; в старом алфавите, где &amp;lt;tex&amp;gt; i \bmod 3 = 1 &amp;lt;/tex&amp;gt;, в новом алфавите будет соответствовать строка &amp;lt;tex&amp;gt; S'\left[\dfrac{i-1}{3}..\dfrac{n}{3} - 1\right] &amp;lt;/tex&amp;gt;, а если &amp;lt;tex&amp;gt; i\bmod 3 = 2 &amp;lt;/tex&amp;gt;, то строка &amp;lt;tex&amp;gt; S'\left[\dfrac{n}{3} + \dfrac{i-2}{3}..\dfrac{2n}{3} - 1\right] &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Вызовем алгоритм рекурсивно для строки &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt;, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# Пройдем по массиву &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt; A_{S'}[i] &amp;lt; \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j = 3A_{S'}[i] + 1 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;, если же &amp;lt;tex&amp;gt; A_{S'}[i] \geqslant \dfrac{n}{3} &amp;lt;/tex&amp;gt;, то этот суффикс соответствует позиции &amp;lt;tex&amp;gt; j =  3\left(A_{S'}[i] - \dfrac{n}{3}\right) + 2 &amp;lt;/tex&amp;gt; в строке &amp;lt;tex&amp;gt; S &amp;lt;/tex&amp;gt;. Псевдокод получения &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;:&lt;br /&gt;
 &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 '''for''' i = 0 '''to''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;.length - 1:&lt;br /&gt;
    '''if''' &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] &amp;lt; n/3:&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * &amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] + 1)&lt;br /&gt;
    '''else''':&lt;br /&gt;
        &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;.add(3 * (&amp;lt;tex&amp;gt;A_{S'}&amp;lt;/tex&amp;gt;[i] - n/3) + 2)&lt;br /&gt;
&lt;br /&gt;
=== Шаг 2 ===&lt;br /&gt;
Этот шаг также аналогичен первой версии алгоритма. Сортировка множества &amp;lt;tex&amp;gt; \{ S[i..n-1] | i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt; аналогична сортировке пар &amp;lt;tex&amp;gt; \{ (S[i], S[i+1..n-1]) | i \bmod 3 = 0 \} &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; S[i+1..n-1] &amp;lt;/tex&amp;gt; — суффиксы в позициях, равных 1 по модулю 3, относительный порядок которых уже известен. Выпишем эти пары в порядке вхождения их в &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и отсортируем по первому элементу устойчивой сортировкой подсчетом, получив суффиксный массив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;. Псевдокод этого шага:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 M = []&lt;br /&gt;
 '''for''' i = 0 '''to''' 2n/3 - 1:&lt;br /&gt;
     '''if''' &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[i] % 3 == 1:&lt;br /&gt;
         M.add(Pair(S[&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i] - 1], &amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;[i]))&lt;br /&gt;
 stable_sort(M)&lt;br /&gt;
 '''for''' i = 0 '''to''' n/3 - 1:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S_0}&amp;lt;/tex&amp;gt;.add(M[i].second - 1)&lt;br /&gt;
Аналогично, второй шаг требует &amp;lt;tex&amp;gt; O(n) &amp;lt;/tex&amp;gt; времени.&lt;br /&gt;
&lt;br /&gt;
=== Шаг 3 ===&lt;br /&gt;
На этом шаге мы должны слить суффиксные массивы &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, чтобы получить суффиксный массив &amp;lt;tex&amp;gt; A_{S} &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;
&lt;br /&gt;
Пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 1 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Это аналогично сравнению пар &amp;lt;tex&amp;gt; (S[i], S[i+1..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S[j], S[j+1..n-1]) &amp;lt;/tex&amp;gt;. Сравнить первые элементы пар мы можем за &amp;lt;tex&amp;gt; O(1) &amp;lt;/tex&amp;gt;, а относительный порядок вторых элементов пар нам уже известен, так как они соответствуют позициям, равным 2 и 1 по модулю 3 соответственно.&lt;br /&gt;
&lt;br /&gt;
Аналогично, пусть на какой-то итерации слияния мы сравниваем суффиксы, соответствующие позициям &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt;, равной 2 по модулю 3, и &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; (она всегда будет равна 0 по модулю 3). Тогда это аналогично сравнению троек &amp;lt;tex&amp;gt; (S[i], S[i+1], S[i+2..n-1]) &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; (S[j], S[j+1], S[j+2..n-1]) &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;A_{S}&amp;lt;/tex&amp;gt; = []&lt;br /&gt;
 // Вначале предподсчитаем за O(n) обратную перестановку для суффиксного массива &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;, то есть массив rank такой, что &amp;lt;tex&amp;gt; A_{S_{12}}&amp;lt;/tex&amp;gt;[rank[i]] = i.&lt;br /&gt;
 // Тогда мы сможем за O(1) сравнивать суффиксы по их позиции.&lt;br /&gt;
 rank = inverse(&amp;lt;tex&amp;gt;A_{S_{12}}&amp;lt;/tex&amp;gt;) &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3 '''and''' j &amp;lt; n/3:&lt;br /&gt;
     pos12 = &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i]&lt;br /&gt;
     pos0  = &amp;lt;tex&amp;gt; A_{0} &amp;lt;/tex&amp;gt;[j]&lt;br /&gt;
     '''if''' pos12 % 3 == 1:&lt;br /&gt;
        ''' if''' Pair(S[pos12], rank[pos12 + 1]) &amp;lt; Pair(S[pos0], rank[pos0 + 1]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++  &lt;br /&gt;
     '''else''':&lt;br /&gt;
         '''if''' Triple(S[pos12], S[pos12 + 1], rank[pos12 + 2]) &amp;lt; Triple(S[pos0], S[pos0 + 1], rank[pos0 + 2]):&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos12)&lt;br /&gt;
             i++&lt;br /&gt;
         '''else''':&lt;br /&gt;
             &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(pos0)&lt;br /&gt;
             j++ &lt;br /&gt;
 '''while''' i &amp;lt; 2 * n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;[i])&lt;br /&gt;
     i++&lt;br /&gt;
 '''while''' j &amp;lt; n/3:&lt;br /&gt;
     &amp;lt;tex&amp;gt;A_{S}&amp;lt;/tex&amp;gt;.add(&amp;lt;tex&amp;gt; A_{S_{0}} &amp;lt;/tex&amp;gt;[j])&lt;br /&gt;
     j++&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;
Построим суффиксный массив для строки '''abbacab'''. После добавления защитного символа и дополнения до кратной трем длины, получим '''abbacab$$'''.&lt;br /&gt;
==== Шаг 1 ====&lt;br /&gt;
# Тройками, соответствующими равными 1 по модулю 3 позициям, будут: '''bba''', '''cab''', '''$$$''', соответствующими равным 2 по модулю 3 — '''bac''', '''ab$''', '''$$$'''. Новый алфавит &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет содержать элементы '''bba''', '''cab''', '''$$$''', '''bac''', '''ab$''', которые после сортировки получат номера 3, 4, 0, 2, 1 соотвествтенно.&lt;br /&gt;
# Строкой '''bbacab$$$bacab$$$$''' в новом алфавите &amp;lt;tex&amp;gt; \Sigma' &amp;lt;/tex&amp;gt; будет &amp;lt;tex&amp;gt; S' &amp;lt;/tex&amp;gt; = '''340210'''. &lt;br /&gt;
# После рекурсивного вызова получим &amp;lt;tex&amp;gt; A_{S'} &amp;lt;/tex&amp;gt; = [5, 2, 4, 3, 0, 1]. Пересчитав &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt;, получим [(5 - 3)*3 + 2, 2 * 3 + 1, (4 - 3) * 3 + 2, (3 - 3) * 3 + 2, 0 * 3 + 1, 1 * 3 + 1] = [8, 7, 5, 2, 1, 4].&lt;br /&gt;
&lt;br /&gt;
==== Шаг 2 ====&lt;br /&gt;
# Обойдя массив &amp;lt;tex&amp;gt; A_{S_{12}} &amp;lt;/tex&amp;gt; и выбрав в нем элементы, равные 1 по модулю 3, получим массив пар &amp;lt;tex&amp;gt;M&amp;lt;/tex&amp;gt; = [('''b''', 7), ('''a''', 1), ('''a''', 4)]&lt;br /&gt;
# После устойчивой сортировки подсчетом по первому элементу, получим &amp;lt;tex&amp;gt; M &amp;lt;/tex&amp;gt; = [('''a''', 1), ('''a''', 4), ('''b''', 7)]&lt;br /&gt;
# Восстановив &amp;lt;tex&amp;gt; A_{S_0} &amp;lt;/tex&amp;gt;, получаем [0, 3, 6].&lt;br /&gt;
==== Шаг 3 ====&lt;br /&gt;
Рассмотрим, к примеру, третью итерацию слияния, к этой итерации массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 2, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0, на ней мы сливаем суффиксы, соответствующие позициям 5 и 0. &lt;br /&gt;
# Образуем тройки (S[5], S[6], S[7..8]) и (S[0], S[1], S[2..8]).&lt;br /&gt;
# После получения относительного порядка суффиксов, получим тройки ('''a''', '''b''', 1) и ('''a''', '''b''', 3). Первая тройка меньше второй, поэтому добавляем суффикс, соответствующий позиции 5 в массив &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;.&lt;br /&gt;
# В конце итерации получаем &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5], &amp;lt;tex&amp;gt; i &amp;lt;/tex&amp;gt; = 3, &amp;lt;tex&amp;gt; j &amp;lt;/tex&amp;gt; = 0.&lt;br /&gt;
К концу слияния получим &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [8, 7, 5, 0, 3, 6, 2, 1, 4]. Так как мы добавляли один символ '''$''' в начале алгоритма для дополнения строки до длины, кратной трем, выбросим последний суффикс из &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt;, получим в итоге, что &amp;lt;tex&amp;gt; A_{S} &amp;lt;/tex&amp;gt; = [7, 5, 0, 3, 6, 2, 1, 4].&lt;br /&gt;
 8 '''$''' (будет выброшен, так как в начале алгоритма добавляли один '''$''' к строке)&lt;br /&gt;
 7 '''$$'''&lt;br /&gt;
 5 '''ab$$'''&lt;br /&gt;
 0 '''abbacab$$'''&lt;br /&gt;
 3 '''acab$$'''&lt;br /&gt;
 6 '''b$$'''&lt;br /&gt;
 2 '''bacab$$'''&lt;br /&gt;
 1 '''bbacab$$'''&lt;br /&gt;
 4 '''cab$$'''&lt;br /&gt;
{|&lt;br /&gt;
| [[Файл:Kark_sanders_stage1.png|325px|thumb| Фаза 1]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage2.png|325px|thumb| Фаза 2]]&lt;br /&gt;
| [[Файл:Kark_sanders_stage3.png|325px|thumb| Фаза 3]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Получение массива LCP ==&lt;br /&gt;
Массив LCP можно получить за линейное время [[Алгоритм_Касаи_и_др. | алгоритмом Касаи]].&lt;br /&gt;
&lt;br /&gt;
== Обобщение алгоритма ==&lt;br /&gt;
На самом деле, алгоритм можно обобщить&amp;lt;ref name=&amp;quot;generalisation&amp;quot;&amp;gt; Juha Kärkkäinen, Peter Sanders and Stefan Burkhardt. Linear work suffix array construction. http://www.cs.helsinki.fi/juha.karkkainen/publications/jacm05-revised.pdf &amp;lt;/ref&amp;gt;, взяв на первом шаге, к примеру, суффиксы, позиции которых по модулю 7 дают 3, 5 и 6. Для этого потребуются некоторое усложнение алгоритма, например, сортировка оставшихся суффиксов в нескольких группах на шаге 2 и слияние нескольких групп на шаге 3, но основная идея алгоритма остается той же. Множества, которые можно выбрать, на первом шаге определяются '''разностным покрытием''' (''difference cover'').&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Разностное покрытие''' (''difference cover'') &amp;lt;tex&amp;gt; D &amp;lt;/tex&amp;gt; по модулю &amp;lt;tex&amp;gt;m &amp;lt;/tex&amp;gt; — множество чисел от &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;m - 1 &amp;lt;/tex&amp;gt; таких, что &amp;lt;tex&amp;gt; \forall i \in [0, m-1]: \exists j, k \in D: i \equiv k - j \pmod m &amp;lt;/tex&amp;gt;.&lt;br /&gt;
}} &lt;br /&gt;
Например, &amp;lt;tex&amp;gt; \{1, 2\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 3 &amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt; \{3, 5, 6\} &amp;lt;/tex&amp;gt; является разностным покрытием по модулю &amp;lt;tex&amp;gt; 7 &amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt; \{1\} &amp;lt;/tex&amp;gt; — не является разностным покрытием по модулю &amp;lt;tex&amp;gt; 2 &amp;lt;/tex&amp;gt;, поэтому этот алгоритм не применим к нему. Подробнее узнать, как вычислять разностное покрытие для заданного модуля можно также здесь&amp;lt;ref name=&amp;quot;generalisation&amp;quot;/&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Источники информации==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
* [http://www.cs.helsinki.fi/juha.karkkainen/publications/icalp03.pdf Juha Kärkkäinen and Peter Sanders. Simple linear work suffix array construction]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Суффиксный массив]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8_RMQ_%D0%BA_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B5_LCA&amp;diff=47428</id>
		<title>Сведение задачи RMQ к задаче LCA</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8_RMQ_%D0%BA_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B5_LCA&amp;diff=47428"/>
				<updated>2015-06-04T11:03:14Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Сложность */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Шаблон:Задача&lt;br /&gt;
|definition =Дан массив &amp;lt;tex&amp;gt;A[1..N]&amp;lt;/tex&amp;gt;. Поступают запросы вида &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, на каждый запрос требуется найти минимум в массиве &amp;lt;tex&amp;gt;A&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;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Алгоритм ==&lt;br /&gt;
[[Файл:Wiki.PNG|thumb|right|300x150px|Пример декартового дерева]]&lt;br /&gt;
Для нахождения минимума на отрезке будем использовать информацию о наименьшем общем предке. Для хранения наименьших общих предков будем использовать  [[декартово дерево по неявному ключу]].&lt;br /&gt;
&lt;br /&gt;
Декартово дерево по неявному ключу на массиве &amp;lt;tex&amp;gt;A[1..N]&amp;lt;/tex&amp;gt; {{---}} это бинарное дерево, допускающее следующее рекурсивное построение:&lt;br /&gt;
* Корнем дерева является элемент массива, имеющий минимальное значение &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;, скажем &amp;lt;tex&amp;gt;A[i]&amp;lt;/tex&amp;gt;. Если минимальных элементов несколько, можно взять любой.&lt;br /&gt;
* Левым поддеревом является декартово дерево на массиве &amp;lt;tex&amp;gt;A[1..i-1]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
* Правым поддеревом является декартово дерево на массиве &amp;lt;tex&amp;gt;A[i+1..N]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Здесь и далее &amp;lt;tex&amp;gt;A[i]&amp;lt;/tex&amp;gt; будет также использоваться для обозначения соответствующей вершины дерева.&lt;br /&gt;
&lt;br /&gt;
Построим декартово дерево на массиве &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;. Тогда &amp;lt;tex&amp;gt;RMQ(i, j)&amp;lt;/tex&amp;gt; = &amp;lt;tex&amp;gt;LCA(A[i], A[j])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&amp;lt;br clear=&amp;quot;all&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Доказательство ==&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement=&lt;br /&gt;
&amp;lt;tex&amp;gt;RMQ(i, j)&amp;lt;/tex&amp;gt; = &amp;lt;tex&amp;gt;LCA(A[i], A[j])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
Положим &amp;lt;tex&amp;gt;w = LCA(A[i], A[j])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что &amp;lt;tex&amp;gt;A[i]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;A[j]&amp;lt;/tex&amp;gt; не принадлежат одновременно либо правому, либо левому поддереву &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;, потому как тогда бы соответствующий сын находился на большей глубине, чем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;, и также являлся предком как &amp;lt;tex&amp;gt;A[i]&amp;lt;/tex&amp;gt; так и &amp;lt;tex&amp;gt;A[j]&amp;lt;/tex&amp;gt;, что противоречит определению &amp;lt;tex&amp;gt;LCA&amp;lt;/tex&amp;gt;. Из этого замечанию следует, что &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt; лежит между &amp;lt;tex&amp;gt;A[i]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;A[j]&amp;lt;/tex&amp;gt; и, следовательно, принадлежит отрезку &amp;lt;tex&amp;gt;A[i..j]&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;w&amp;lt;/tex&amp;gt; содержит в себе подмассив &amp;lt;tex&amp;gt;A[i..j]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Суммируя, получаем, что &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt; имеет минимальное значение на отрезке, покрывающем &amp;lt;tex&amp;gt;A[i..j]&amp;lt;/tex&amp;gt;, и принадлежит отрезку &amp;lt;tex&amp;gt;A[i..j]&amp;lt;/tex&amp;gt;, отсюда &amp;lt;tex&amp;gt;RMQ(i, j) = w&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;. Используя [[Сведение задачи LCA к задаче RMQ | алгоритм построения LCA]], получаем:&lt;br /&gt;
препроцессинг для &amp;lt;tex&amp;gt;LCA&amp;lt;/tex&amp;gt; {{---}} &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; и ответ на запрос {{---}} &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&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(1)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
В итоге получили &amp;lt;tex&amp;gt;RMQ&amp;lt;/tex&amp;gt; с построением за &amp;lt;tex&amp;gt;O(n)&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;
*[[Сведение задачи LCA к задаче RMQ]]&lt;br /&gt;
*[[Декартово дерево]]&lt;br /&gt;
*[[Алгоритм Фарака-Колтона и Бендера]]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Задача о наименьшем общем предке]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8_LCA_%D0%BA_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B5_RMQ&amp;diff=47427</id>
		<title>Сведение задачи LCA к задаче RMQ</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8_LCA_%D0%BA_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B5_RMQ&amp;diff=47427"/>
				<updated>2015-06-04T10:49:43Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Препроцессинг */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Шаблон:Задача&lt;br /&gt;
|definition = &lt;br /&gt;
Пусть дано корневое дерево &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. На вход подаются запросы вида &amp;lt;tex&amp;gt;(u,\;v)&amp;lt;/tex&amp;gt;, для каждого запроса требуется найти их наименьшего общего предка.&lt;br /&gt;
}}&lt;br /&gt;
{{Определение &lt;br /&gt;
|id=lca_suf_tree&lt;br /&gt;
|definition = '''Наименьшим общим предком''' (англ. ''least common ancestor'') двух узлов &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;T&amp;lt;/tex&amp;gt; называется узел &amp;lt;tex&amp;gt;w&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;, имеет наибольшую глубину.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Алгоритм ==&lt;br /&gt;
=== Препроцессинг ===&lt;br /&gt;
Для каждой вершины &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt; определим глубину с помощью следующей рекурсивной формулы:&lt;br /&gt;
:&amp;lt;tex&amp;gt;\mathrm{depth}(u) = \begin{cases}&lt;br /&gt;
0 &amp;amp; u = \mathrm{root}(T),\\&lt;br /&gt;
\mathrm{depth}(v) + 1 &amp;amp; u = \mathrm{son}(v)&lt;br /&gt;
\end{cases}.&amp;lt;/tex&amp;gt;&lt;br /&gt;
Ясно, что глубина вершины элементарным образом поддерживается во время обхода в глубину.&lt;br /&gt;
&lt;br /&gt;
Запустим [[Обход в глубину, цвета вершин|обход в глубину]] из корня, который будет вычислять значения следующих величин: &lt;br /&gt;
#Cписок глубин посещенных вершин &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;. Глубина текущей вершины добавляется в конец списка при входе в данную вершину, а также после каждого возвращения из её сына.&lt;br /&gt;
#Список посещений узлов &amp;lt;tex&amp;gt;\mathtt{vtx}&amp;lt;/tex&amp;gt;, строящийся аналогично предыдущему, только добавляется не глубина а сама вершина.&lt;br /&gt;
#Значение функции &amp;lt;tex&amp;gt;\mathtt{I}[u]&amp;lt;/tex&amp;gt;, возвращающей любой индекс в списке глубин &amp;lt;tex&amp;gt;d&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;
Будем считать, что &amp;lt;tex&amp;gt;\mathrm{rmq}(d,l,r)&amp;lt;/tex&amp;gt; возвращает индекс минимального элемента в &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; на отрезке &amp;lt;tex&amp;gt;[l..r]&amp;lt;/tex&amp;gt;. Тогда ответом на запрос &amp;lt;tex&amp;gt;\mathrm{lca}(u, v)&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;I[u] \leqslant \mathtt{I}[v]&amp;lt;/tex&amp;gt;, будет &amp;lt;tex&amp;gt;\mathtt{vtx}[\mathrm{rmq}(d,\mathtt{I}[u], \mathtt{I}[v])]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Доказательство корректности алгоритма ===&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement=&lt;br /&gt;
Наименьшему общему предку вершин &amp;lt;tex&amp;gt;u, v&amp;lt;/tex&amp;gt; соответствует минимальная глубина на отрезке &amp;lt;tex&amp;gt;d[\mathtt{I}[u], \mathtt{I}[v]]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
Рассмотрим два узла &amp;lt;tex&amp;gt;u, v&amp;lt;/tex&amp;gt; корневого дерева &amp;lt;tex&amp;gt;T&amp;lt;/tex&amp;gt;. Рассмотрим отрезок &amp;lt;tex&amp;gt;d[\mathtt{I}[u]..\mathtt{I}[v]]&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;w&amp;lt;/tex&amp;gt; (в дереве есть только один простой путь между вершинами), а следовательно минимум на отрезке никак не больше глубины &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Заметим, что в момент добавления &amp;lt;tex&amp;gt;\mathtt{I}[u]&amp;lt;/tex&amp;gt; обход посещал поддерево с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. В момент добавления &amp;lt;tex&amp;gt;\mathtt{I}[v]&amp;lt;/tex&amp;gt; мы все еще в поддереве с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Значит, и на отрезке между &amp;lt;tex&amp;gt;\mathtt{I}[u]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;\mathtt{I}[v]&amp;lt;/tex&amp;gt; мы находились внутри поддерева с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;. Отсюда сделаем заключение, что на рассматриваемом отрезке не посещалась вершина, отличная от &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;, с глубиной меньшей либо равной глубины &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;, т. к. подобной вершины нет в поддереве с корнем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;.&lt;br /&gt;
}}.&lt;br /&gt;
&lt;br /&gt;
== Пример ==&lt;br /&gt;
Рассмотрим дерево на рисунке 1. Найдем наименьшего общего предка вершин, помеченных красным цветом.&lt;br /&gt;
Список глубин, получающийся в результате обхода в глубину {{---}} &amp;lt;tex&amp;gt;[0, 1, 2, 1, 2, 1, 0, 1, 0].&amp;lt;/tex&amp;gt;&lt;br /&gt;
Глубина наименьшего общего предка красных вершин равна минимуму на отрезке &amp;lt;tex&amp;gt;[2, 1, 0, 1].&amp;lt;/tex&amp;gt;&lt;br /&gt;
[[Файл:Lca to rmq.png(2).png|thumb|left|Рисунок к примеру]]&lt;br /&gt;
&amp;lt;div style='clear:left;'&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Сложность ==&lt;br /&gt;
&lt;br /&gt;
Для нахождения минимального элемента на отрезке можно использовать [[Алгоритм Фарака-Колтона и Бендера|алгоритм Фарака-Колтона и Бендера]] для &amp;lt;tex&amp;gt;\pm 1RMQ&amp;lt;/tex&amp;gt;, т. к. соседние элементы в списке глубин отличаются не более чем на единицу. Длина списка глубин составляет &amp;lt;tex&amp;gt;(2n - 1)&amp;lt;/tex&amp;gt;, таким образом, препроцессинг работает за &amp;lt;tex&amp;gt;O(n)&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;
*[[Решение RMQ с помощью разреженной таблицы]]&lt;br /&gt;
*[[Алгоритм Фарака-Колтона и Бендера]]&lt;br /&gt;
*[[Сведение задачи RMQ к задаче LCA]]&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
*[http://e-maxx.ru/algo/lca Наименьший общий предок. Нахождение за O (sqrt (N)) и O (log N) с препроцессингом O (N)]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Задача о наименьшем общем предке]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8_RMQ_%D0%BA_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B5_LCA&amp;diff=47425</id>
		<title>Сведение задачи RMQ к задаче LCA</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B8_RMQ_%D0%BA_%D0%B7%D0%B0%D0%B4%D0%B0%D1%87%D0%B5_LCA&amp;diff=47425"/>
				<updated>2015-06-04T10:44:38Z</updated>
		
		<summary type="html">&lt;p&gt;Nastyastanchul: /* Алгоритм */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Шаблон:Задача&lt;br /&gt;
|definition =Дан массив &amp;lt;tex&amp;gt;A[1..N]&amp;lt;/tex&amp;gt;. Поступают запросы вида &amp;lt;tex&amp;gt;(i, j)&amp;lt;/tex&amp;gt;, на каждый запрос требуется найти минимум в массиве &amp;lt;tex&amp;gt;A&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;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Алгоритм ==&lt;br /&gt;
[[Файл:Wiki.PNG|thumb|right|300x150px|Пример декартового дерева]]&lt;br /&gt;
Для нахождения минимума на отрезке будем использовать информацию о наименьшем общем предке. Для хранения наименьших общих предков будем использовать  [[декартово дерево по неявному ключу]].&lt;br /&gt;
&lt;br /&gt;
Декартово дерево по неявному ключу на массиве &amp;lt;tex&amp;gt;A[1..N]&amp;lt;/tex&amp;gt; {{---}} это бинарное дерево, допускающее следующее рекурсивное построение:&lt;br /&gt;
* Корнем дерева является элемент массива, имеющий минимальное значение &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;, скажем &amp;lt;tex&amp;gt;A[i]&amp;lt;/tex&amp;gt;. Если минимальных элементов несколько, можно взять любой.&lt;br /&gt;
* Левым поддеревом является декартово дерево на массиве &amp;lt;tex&amp;gt;A[1..i-1]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
* Правым поддеревом является декартово дерево на массиве &amp;lt;tex&amp;gt;A[i+1..N]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Здесь и далее &amp;lt;tex&amp;gt;A[i]&amp;lt;/tex&amp;gt; будет также использоваться для обозначения соответствующей вершины дерева.&lt;br /&gt;
&lt;br /&gt;
Построим декартово дерево на массиве &amp;lt;tex&amp;gt;A&amp;lt;/tex&amp;gt;. Тогда &amp;lt;tex&amp;gt;RMQ(i, j)&amp;lt;/tex&amp;gt; = &amp;lt;tex&amp;gt;LCA(A[i], A[j])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&amp;lt;br clear=&amp;quot;all&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Доказательство ==&lt;br /&gt;
{{Теорема&lt;br /&gt;
|statement=&lt;br /&gt;
&amp;lt;tex&amp;gt;RMQ(i, j)&amp;lt;/tex&amp;gt; = &amp;lt;tex&amp;gt;LCA(A[i], A[j])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
|proof=&lt;br /&gt;
Положим &amp;lt;tex&amp;gt;w = LCA(A[i], A[j])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Заметим, что &amp;lt;tex&amp;gt;A[i]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;A[j]&amp;lt;/tex&amp;gt; не принадлежат одновременно либо правому, либо левому поддереву &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;, потому как тогда бы соответствующий сын находился на большей глубине, чем &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt;, и также являлся предком как &amp;lt;tex&amp;gt;A[i]&amp;lt;/tex&amp;gt; так и &amp;lt;tex&amp;gt;A[j]&amp;lt;/tex&amp;gt;, что противоречит определению &amp;lt;tex&amp;gt;LCA&amp;lt;/tex&amp;gt;. Из этого замечанию следует, что &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt; лежит между &amp;lt;tex&amp;gt;A[i]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;A[j]&amp;lt;/tex&amp;gt; и, следовательно, принадлежит отрезку &amp;lt;tex&amp;gt;A[i..j]&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;w&amp;lt;/tex&amp;gt; содержит в себе подмассив &amp;lt;tex&amp;gt;A[i..j]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Суммируя, получаем, что &amp;lt;tex&amp;gt;w&amp;lt;/tex&amp;gt; имеет минимальное значение на отрезке, покрывающем &amp;lt;tex&amp;gt;A[i..j]&amp;lt;/tex&amp;gt;, и принадлежит отрезку &amp;lt;tex&amp;gt;A[i..j]&amp;lt;/tex&amp;gt;, отсюда &amp;lt;tex&amp;gt;RMQ(i, j) = w&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;. Используя [[Сведение задачи LCA к задаче RMQ]], получаем:&lt;br /&gt;
препроцессинг для &amp;lt;tex&amp;gt;LCA&amp;lt;/tex&amp;gt; {{---}} &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; и ответ на запрос {{---}} &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
В итоге получили &amp;lt;tex&amp;gt;RMQ&amp;lt;/tex&amp;gt; с построением за &amp;lt;tex&amp;gt;O(n)&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;
*[[Сведение задачи LCA к задаче RMQ]]&lt;br /&gt;
*[[Декартово дерево]]&lt;br /&gt;
*[[Алгоритм Фарака-Колтона и Бендера]]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Задача о наименьшем общем предке]]&lt;/div&gt;</summary>
		<author><name>Nastyastanchul</name></author>	</entry>

	</feed>