<?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=37.146.229.100&amp;*</id>
		<title>Викиконспекты - Вклад участника [ru]</title>
		<link rel="self" type="application/atom+xml" href="http://neerc.ifmo.ru/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=37.146.229.100&amp;*"/>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%BB%D1%83%D0%B6%D0%B5%D0%B1%D0%BD%D0%B0%D1%8F:%D0%92%D0%BA%D0%BB%D0%B0%D0%B4/37.146.229.100"/>
		<updated>2026-04-15T13:32:32Z</updated>
		<subtitle>Вклад участника</subtitle>
		<generator>MediaWiki 1.30.0</generator>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D1%83%D1%84%D1%84%D0%B8%D0%BA%D1%81%D0%BD%D1%8B%D0%B9_%D0%BC%D0%B0%D1%81%D1%81%D0%B8%D0%B2&amp;diff=70666</id>
		<title>Суффиксный массив</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D1%83%D1%84%D1%84%D0%B8%D0%BA%D1%81%D0%BD%D1%8B%D0%B9_%D0%BC%D0%B0%D1%81%D1%81%D0%B8%D0%B2&amp;diff=70666"/>
				<updated>2019-04-01T09:36:29Z</updated>
		
		<summary type="html">&lt;p&gt;37.146.229.100: /* Псевдокод */ записываем в строку s по индексу sa[i], а не по i&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Cуффиксным массивом''' (англ. ''suffix array'') строки &amp;lt;tex&amp;gt;s[1 .. n]&amp;lt;/tex&amp;gt; называется массив &amp;lt;tex&amp;gt;suf&amp;lt;/tex&amp;gt; целых чисел от &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;, такой, что суффикс &amp;lt;tex&amp;gt;s[suf[i]..n]&amp;lt;/tex&amp;gt; — &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-й в [[Лексикографический_порядок|лексикографическом]] порядке среди всех непустых суффиксов строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;.}}&lt;br /&gt;
&lt;br /&gt;
== Пример ==&lt;br /&gt;
&amp;lt;tex&amp;gt;s = abacaba&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл:SuffixArray.png|500px]]&lt;br /&gt;
&lt;br /&gt;
Значит, суффиксный массив для строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; равен &amp;lt;tex&amp;gt;[7, 5, 1, 3, 6, 2, 4]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Восстановление строки по суффиксному массиву ==&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Дан суффиксный массив некоторой строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;, необходимо восстановить строку за время &amp;lt;tex&amp;gt;O(|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;-й в лексикографическом порядке суффикс сопоставить с &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-й буквой в алфавите.&lt;br /&gt;
&lt;br /&gt;
==== Доказательство корректности ====&lt;br /&gt;
Если отсортировать суффиксы, то первые буквы будут расположены в том же порядке, как и в алфавите.&lt;br /&gt;
&lt;br /&gt;
==== Псевдокод ====&lt;br /&gt;
 '''string''' fromSuffixArrayToString('''int[]''' sa):&lt;br /&gt;
   '''for''' i = 1 '''to''' n&lt;br /&gt;
        s[sa[i]] = alphabet[i] &lt;br /&gt;
   '''return''' s&lt;br /&gt;
&lt;br /&gt;
=== Вариант для минимально возможного ===&lt;br /&gt;
Для начала вместо каждого символа строки поставим символ из бесконечного алфавита в промежуточную строку &amp;lt;tex&amp;gt;tmp&amp;lt;/tex&amp;gt;, как в решении выше. Пусть, мы рассматриваем &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-й в лексикографическом порядке суффикс (т.е. и &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-й символ строки). Его первый символ будет равен первому символу предущего в лексикографическом порядке суффикса, если &amp;lt;tex&amp;gt;tmp[sa[i - 1] + 1] &amp;lt; tmp[sa[i] + 1]&amp;lt;/tex&amp;gt;, т.е. и их строки без первого символа так же в лексикографическом порядке. Иначе он должен быть больше, т.к. рассматриваемый суффикс следующий в лексикографическом порядке.&lt;br /&gt;
&lt;br /&gt;
==== Пример ====&lt;br /&gt;
Дан суффиксный массив &amp;lt;tex&amp;gt;[7, 5, 1, 3, 6, 2, 4]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Цветами показаны места, после которых добавляются новые символы.&lt;br /&gt;
&lt;br /&gt;
[[Файл:ExampleSuffixArray.png|center]]&lt;br /&gt;
&lt;br /&gt;
==== Псевдокод ====&lt;br /&gt;
 '''string''' fromSuffixArrayToString('''int[]''' sa):&lt;br /&gt;
   '''for''' i = 1 '''to''' n&lt;br /&gt;
        tmp[sa[i]] = alphabet[i]&lt;br /&gt;
   cur = 1&lt;br /&gt;
   s[sa[1]] = alphabet[1]&lt;br /&gt;
   '''for''' i = 2 '''to''' n&lt;br /&gt;
        j = sa[i - 1]&lt;br /&gt;
        k = sa[i]&lt;br /&gt;
        '''if''' tmp[j + 1] &amp;gt; tmp[k + 1] &lt;br /&gt;
            cur++&lt;br /&gt;
        s[sa[i]] = alphabet[cur]       &lt;br /&gt;
   '''return''' s&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;
{{main|Алгоритм поиска подстроки в строке с помощью суффиксного массива}}&lt;br /&gt;
&lt;br /&gt;
=== Подсчёт LCP для лексикографически соседних суффиксов ===&lt;br /&gt;
&lt;br /&gt;
{{main|Алгоритм Касаи и др.}}&lt;br /&gt;
&lt;br /&gt;
=== Число различных подстрок в строке ===&lt;br /&gt;
&lt;br /&gt;
Вычисление числа различных подстрок в строке за время &amp;lt;tex&amp;gt;O(|s| \log(|s|))&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;O(|s|)&amp;lt;/tex&amp;gt; дополнительной памяти с использованием [[Алгоритм_Касаи_и_др.|LCP]]&amp;lt;ref name=&amp;quot;ref1&amp;quot;&amp;gt;[http://e-maxx.ru/algo/suffix_array#8 MAXimal :: algo :: Суффиксный массив :: Количество различных подстрок]&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Максимальная по длине ветвящаяся влево и вправо строка ===&lt;br /&gt;
&lt;br /&gt;
Данная задача также может быть [[Сжатое_суффиксное_дерево#Поиск строки максимальной длины, ветвящейся влево и вправо|решена]] при помощи [[Сжатое_суффиксное_дерево|суффиксного дерева]].&lt;br /&gt;
&lt;br /&gt;
=== Самая длинная строка p, входящая в t дважды и не пересекаясь ===&lt;br /&gt;
&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition=&lt;br /&gt;
Поиск самой длинной строки &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt;, входящей в строку &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; дважды и не пересекаясь.}}&lt;br /&gt;
==== Основные положения ====&lt;br /&gt;
Построим суффиксный массив строки &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; и посчитаем на нем [[Алгоритм_Касаи_и_др.|LCP]].&lt;br /&gt;
Для суффикса &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; символом &amp;lt;tex&amp;gt;s'&amp;lt;/tex&amp;gt; будем обозначать индекс этого суффикса в суффиксном массиве.&lt;br /&gt;
&lt;br /&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;t&amp;lt;/tex&amp;gt; такие, что &amp;lt;tex&amp;gt;i' \leqslant 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;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;i&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; соответствуют строке &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;, если &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; входит в &amp;lt;tex&amp;gt;t&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;
Для произвольной строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; и двух суффиксов, соответствующих ей, введем два условия:&lt;br /&gt;
# &amp;lt;tex&amp;gt;\max(|i|, |j|) \geqslant \min(|i|, |j|) + |s|&amp;lt;/tex&amp;gt;&lt;br /&gt;
# &amp;lt;tex&amp;gt;|s| = \min\limits_{k=i'\dots j'}lcp[k]&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Утверждение&lt;br /&gt;
|statement=&lt;br /&gt;
Строка &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; входит в &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; дважды и не пересекаясь тогда и только тогда, когда она удовлетворяет условию 1.&lt;br /&gt;
|proof= &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;t&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;|s|&amp;lt;/tex&amp;gt; длиннее другого. Т.е. условие 1 выполнено.&lt;br /&gt;
&lt;br /&gt;
'''Достаточное условие:'''&lt;br /&gt;
&lt;br /&gt;
Из того, что выполняется условие 1 следует, что один из суффиксов хотя бы на &amp;lt;tex&amp;gt;|s|&amp;lt;/tex&amp;gt; длиннее другого. При этом они оба начинаются со строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;. Поэтому строка &amp;lt;tex&amp;gt;s&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;
{{Утверждение&lt;br /&gt;
|statement=&lt;br /&gt;
Если строка &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; является максимальной входящей в &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; дважды, то она удовлетворяет условию 2.&lt;br /&gt;
|proof=&lt;br /&gt;
Пусть это не так и &amp;lt;tex&amp;gt;|s| &amp;lt; \min\limits_{k=i'\dots j'}lcp[k]&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;, чего быть не может по построению &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;
# Построим суффиксный массив, посчитаем на нём [[Алгоритм_Касаи_и_др.|LCP]].&lt;br /&gt;
# Переберем все пары &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; такие, что они удовлетворяют условиям 1 и 2 и возьмем среди них максимум по длине строки.&lt;br /&gt;
&lt;br /&gt;
Этот алгоритм можно реализовать за &amp;lt;tex&amp;gt;O(n^3 + \mathrm{SA})&amp;lt;/tex&amp;gt; или за &amp;lt;tex&amp;gt;O(n^2 + \mathrm{SA})&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;\mathrm{SA}&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;t&amp;lt;/tex&amp;gt; такие, что они входят в &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; дважды и удовлетворяют условию 2 при любых &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; {{---}} суффиксы, соответствующие двум любым вхождениям &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;t&amp;lt;/tex&amp;gt; (т.е. не обязательно непересекающимся). Для каждой такой строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; попробуем найти &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, удовлетворяющие условию 1. &lt;br /&gt;
Таким образом, мы рассмотрим все строки, соответствующие условиям 1 и 2, и, следовательно, найдем ответ. Алгоритм корректный.&lt;br /&gt;
&lt;br /&gt;
Заметим теперь, что искомые строки &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; {{---}} это префиксы суффиксов &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt; длины &amp;lt;tex&amp;gt;lcp[k]&amp;lt;/tex&amp;gt;. &lt;br /&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;, удовлетворяющие условию 1, воспользуемся [[Стек|стеком]].&lt;br /&gt;
&lt;br /&gt;
===== Алгоритм =====&lt;br /&gt;
# Будем идти по суффиксному массиву в порядке лексикографической сортировки суффиксов. В стеке будем хранить префиксы уже рассмотренных суффиксов &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt; длины &amp;lt;tex&amp;gt;lcp[k']&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;. Обозначим за &amp;lt;tex&amp;gt;st&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;|st| = lcp[s']&amp;lt;/tex&amp;gt;&amp;lt;br&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;|st| \geqslant lcp[s']&amp;lt;/tex&amp;gt;&amp;lt;br&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;|st| \leqslant lcp[s']&amp;lt;/tex&amp;gt;&amp;lt;br&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;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;i&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; станут удовлетворять условию 1, обновляем ответ.&lt;br /&gt;
&lt;br /&gt;
===== Оценка времени работы =====&lt;br /&gt;
Т.к. подсчёт &amp;lt;tex&amp;gt;lcp&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; операций, то итоговое время работы &amp;lt;tex&amp;gt;O(n + \mathrm{SA})&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;\mathrm{SA}&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;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Источники ==&lt;br /&gt;
* Дэн Гасфилд — Строки, деревья и последовательности в алгоритмах: Информатика и вычислительная биология — СПб.: Невский Диалект; БХВ-Петербург, 2003. — 654 с: ил.&lt;br /&gt;
* [http://e-maxx.ru/algo/suffix_array MAXimal :: algo :: Суффиксный массив]&lt;br /&gt;
* [http://ru.wikipedia.org/wiki/Суффиксный_массив Википедия — Суффиксный массив]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Suffix_array Wikipedia — Suffix array]&lt;br /&gt;
* [http://habrahabr.ru/post/115346/ Habrahabr — Суффиксный массив — удобная замена суффиксного дерева]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория:Суффиксный массив]]&lt;/div&gt;</summary>
		<author><name>37.146.229.100</name></author>	</entry>

	</feed>