<?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=100.12.169.25&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=100.12.169.25&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/100.12.169.25"/>
		<updated>2026-05-03T11:01:48Z</updated>
		<subtitle>Вклад участника</subtitle>
		<generator>MediaWiki 1.30.0</generator>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B5%D0%BD%D0%B8%D0%B5_%D1%81%D1%83%D1%84%D1%84%D0%B8%D0%BA%D1%81%D0%BD%D0%BE%D0%B3%D0%BE_%D0%BC%D0%B0%D1%81%D1%81%D0%B8%D0%B2%D0%B0_%D1%81_%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D1%8C%D1%8E_%D1%81%D1%82%D0%B0%D0%BD%D0%B4%D0%B0%D1%80%D1%82%D0%BD%D1%8B%D1%85_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%B2_%D1%81%D0%BE%D1%80%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%BA%D0%B8&amp;diff=53400</id>
		<title>Построение суффиксного массива с помощью стандартных методов сортировки</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%9F%D0%BE%D1%81%D1%82%D1%80%D0%BE%D0%B5%D0%BD%D0%B8%D0%B5_%D1%81%D1%83%D1%84%D1%84%D0%B8%D0%BA%D1%81%D0%BD%D0%BE%D0%B3%D0%BE_%D0%BC%D0%B0%D1%81%D1%81%D0%B8%D0%B2%D0%B0_%D1%81_%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D1%8C%D1%8E_%D1%81%D1%82%D0%B0%D0%BD%D0%B4%D0%B0%D1%80%D1%82%D0%BD%D1%8B%D1%85_%D0%BC%D0%B5%D1%82%D0%BE%D0%B4%D0%BE%D0%B2_%D1%81%D0%BE%D1%80%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%BA%D0%B8&amp;diff=53400"/>
				<updated>2016-04-21T18:56:37Z</updated>
		
		<summary type="html">&lt;p&gt;100.12.169.25: /* Псевдокод */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Идея построения суффиксного массива ==&lt;br /&gt;
&lt;br /&gt;
Согласно [[Суффиксный массив|определению]] суффиксного массива, для его построения достаточно отсортировать все суффиксы строки. Заменим сортировку суффиксов строки &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt; на сортировку циклических сдвигов строки &amp;lt;tex&amp;gt;\alpha\$&amp;lt;/tex&amp;gt;, где символ &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt; строго меньше любого символа из &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt;. Тогда если в упорядоченных циклических сдвигах отбросить суффикс, начинающийся на &amp;lt;tex&amp;gt;\$&amp;lt;/tex&amp;gt;, то получатся упорядоченные суффиксы исходной строки &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt;. В дальнейшем положим &amp;lt;tex&amp;gt;|\alpha\$| = n &amp;lt;/tex&amp;gt; (заметим, что все циклические сдвиги также имеют длину &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;), а также &amp;lt;tex&amp;gt;\alpha\$ = s&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Наивный алгоритм ==&lt;br /&gt;
&lt;br /&gt;
Данный алгоритм достаточно тривиален. Отсортируем все циклические сдвиги строки &amp;lt;tex&amp;gt;\alpha\$&amp;lt;/tex&amp;gt;, воспользовавшись любым известным методом логарифмической сортировки (например &amp;quot;сортировка слиянием&amp;quot;). Тогда сравнение любых двух циклических сдвигов будет осуществляться за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; и суммарная сложность алгоритма составит &amp;lt;tex&amp;gt;O(n^2\log n)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Псевдокод ===&lt;br /&gt;
&lt;br /&gt;
 '''int[]''' sufArray('''string''' s)&lt;br /&gt;
    suf = {0, 1 .. s.length}&lt;br /&gt;
    '''sort'''(suf, compare)&lt;br /&gt;
    '''return''' suf&lt;br /&gt;
 &lt;br /&gt;
 '''order''' compare('''int''' j1, '''int''' j2)&lt;br /&gt;
     '''for''' i = 0 .. s.length&lt;br /&gt;
         '''if''' (s[(j1 + i) '''mod''' s.length] &amp;gt; s[(j2 + i) '''mod''' s.length])&lt;br /&gt;
             '''return''' ''GT''&lt;br /&gt;
         '''if''' (s[(j1 + i) '''mod''' s.length] &amp;lt; s[(j2 + i) '''mod''' s.length])&lt;br /&gt;
             '''return''' ''LT''&lt;br /&gt;
     '''return''' ''EQ''&lt;br /&gt;
&lt;br /&gt;
== Алгоритм, использующий хеши ==&lt;br /&gt;
&lt;br /&gt;
Данный алгоритм является некоторым улучшением предыдущего. Основная цель {{---}} сократить оценку времени сравнения двух циклических сдвигов до &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;, тогда мы по аналогии с предыдущим алгоритмом получим оценку &amp;lt;tex&amp;gt;O(n \log^2 n)&amp;lt;/tex&amp;gt;. У нас есть возможность быстро сравнивать подстроки на равенство используя метод, описанный в [[Поиск_подстроки_в_строке_с_использованием_хеширования._Алгоритм_Рабина-Карпа |алгоритме Рабина-Карпа ]].&lt;br /&gt;
&lt;br /&gt;
Пусть нам необходимо сравнить два циклических сдвига &amp;lt;tex&amp;gt;s[i_1..i_1-1]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;s[i_2..i_2-1]&amp;lt;/tex&amp;gt;. Найдем сначала их наибольший общий префикс (&amp;lt;tex&amp;gt;lcp(i_1,i_2)&amp;lt;/tex&amp;gt;), для этого будем использовать двоичный поиск по длине совпадающего префикса, а проверку осуществлять с помощью посчитанных хешей префиксов. Поскольку циклический сдвиг состоит из суффикса и префикса &amp;lt;tex&amp;gt;suf + pref&amp;lt;/tex&amp;gt; исходной строки, то с помощью двух хешей префиксов исходной строки можно найти хеш &amp;lt;tex&amp;gt;suf&amp;lt;/tex&amp;gt; или префикса &amp;lt;tex&amp;gt;suf&amp;lt;/tex&amp;gt;. Таким образом можно найти хеш префикса циклического сдвига. &lt;br /&gt;
&lt;br /&gt;
Если оказалось, что &amp;lt;tex&amp;gt;lcp(i_1,i_2) = n&amp;lt;/tex&amp;gt;, то строки равны. Если же &amp;lt;tex&amp;gt;lcp(i_1,i_2) &amp;lt; n&amp;lt;/tex&amp;gt;, то символы &amp;lt;tex&amp;gt;s[i_1 + lcp]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;s[i_2+lcp]&amp;lt;/tex&amp;gt; точно различаются, и их сравнение позволяет сделать вывод, какой из циклических сдвигов меньше в лексикографическом порядке. Итак, двоичный поиск работает за &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;, остальные операции требуют константного времени, следовательно, время, необходимое на сравнение двух циклических сдвигов, оценивается как &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Псевдокод ===&lt;br /&gt;
&lt;br /&gt;
 '''int[]''' sufArray('''string''' s)&lt;br /&gt;
    suf = {0, 1 .. s.length}&lt;br /&gt;
    '''sort'''(suf, compare)&lt;br /&gt;
    '''return''' suf&lt;br /&gt;
 &lt;br /&gt;
 '''order''' compare('''int''' j1, '''int''' j2)&lt;br /&gt;
     same = '''lcp'''(j1, j2)&lt;br /&gt;
     '''if''' s[j1 + same] &amp;lt; s[j2 + same]&lt;br /&gt;
         '''return''' ''LT''&lt;br /&gt;
     '''else if''' s[j1 + same] == s2[j2 + same]&lt;br /&gt;
         '''return''' ''EQ''&lt;br /&gt;
     '''else'''&lt;br /&gt;
         '''return''' ''GT''&lt;br /&gt;
 &lt;br /&gt;
 '''int''' lcp(j1, j2)&lt;br /&gt;
    l = -1&lt;br /&gt;
    r = s.length + 1&lt;br /&gt;
    '''while''' r - l &amp;gt; 1&lt;br /&gt;
        m = (r + l) / 2&lt;br /&gt;
        '''if''' hash[j1 .. j1 + m] == hash[j2 .. j2 + m]&lt;br /&gt;
           l = m&lt;br /&gt;
        '''else'''&lt;br /&gt;
           r = m&lt;br /&gt;
    '''return''' l&lt;br /&gt;
&lt;br /&gt;
== Алгоритм, использующий префиксы циклических сдвигов ==&lt;br /&gt;
&lt;br /&gt;
Этот алгоритм сильно отличается от двух предыдущих и от него несложно перейти к алгоритму за &amp;lt;tex&amp;gt;O(n \log n)&amp;lt;/tex&amp;gt;. Итак, основная идея: на каждом шаге будем сортировать префиксы циклических сдвигов длины &amp;lt;tex&amp;gt;1,2,4,..., 2^{\lceil \log_2 n\rceil}&amp;lt;/tex&amp;gt;. Еще одно важное дополнение: после каждой фазы каждому префиксу циклического сдвига &amp;lt;tex&amp;gt;s[i..i-1]&amp;lt;/tex&amp;gt; будет присваиваться номер класса эквивалентности  &amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; среди этих префиксов. Причем классы эквивалентности должны быть пронумерованы в лексикографическом порядке соответствующих представителей.&lt;br /&gt;
&lt;br /&gt;
Сначала легко можно отсортировать за &amp;lt;tex&amp;gt;O(n \log n)&amp;lt;/tex&amp;gt; префиксы длины &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, то есть символы. А номера классов поставить в соответствии с порядковым номером символа в алфавите.&lt;br /&gt;
&lt;br /&gt;
Рассмотрим теперь переход от префиксов длины &amp;lt;tex&amp;gt;l&amp;lt;/tex&amp;gt; к префиксам длины &amp;lt;tex&amp;gt;2l&amp;lt;/tex&amp;gt;. Научимся сравнивать два префикса длины &amp;lt;tex&amp;gt;2l&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt;: Пусть даны префиксы &amp;lt;tex&amp;gt;s[i..i+2l-1]&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;s[j..j+2l-1]&amp;lt;/tex&amp;gt;, сравним сначала их левые половинки, использовав значения &amp;lt;tex&amp;gt;c[i], c[j]&amp;lt;/tex&amp;gt; с предыдущего шага, если &amp;lt;tex&amp;gt;c[i]\neq c[j]&amp;lt;/tex&amp;gt;, то префиксы соотносятся так как же, как &amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; c[j]&amp;lt;/tex&amp;gt;, если &amp;lt;tex&amp;gt;c[i]=c[j]&amp;lt;/tex&amp;gt;, то переходим к сравнению &amp;lt;tex&amp;gt;c[i+l]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; c[j+l]&amp;lt;/tex&amp;gt;. Итак, отсортировать префиксы длины &amp;lt;tex&amp;gt;2l&amp;lt;/tex&amp;gt; можно за &amp;lt;tex&amp;gt;O(n\log n)&amp;lt;/tex&amp;gt;. Вычислить новые &amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; можно просто пробежавшись в лексикографическом порядке по префиксам, и увеличивая номер соответствующего класса на &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, если текущий префикс не совпадает с предыдущим (сравнивать с помощью старых &amp;lt;tex&amp;gt;c[i], c[i+l]&amp;lt;/tex&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
После шага &amp;lt;tex&amp;gt;l =2^{\lceil \log_2 n\rceil} \geqslant n&amp;lt;/tex&amp;gt; все циклические сдвиги будут отсортированы. Всего шагов &amp;lt;tex&amp;gt;O(\log n)&amp;lt;/tex&amp;gt;, каждый шаг проводится за &amp;lt;tex&amp;gt;O(n \log n)&amp;lt;/tex&amp;gt;, итоговая асимптотика &amp;lt;tex&amp;gt;O(n \log^2 n)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Схожая идея используется и в [[Алгоритм цифровой сортировки суффиксов циклической строки|алгоритме цифровой сортировки суффиксов циклической строки]], который имеет лучшую асимптотику.&lt;br /&gt;
=== Псевдокод ===&lt;br /&gt;
 '''int[]''' suf_array('''string''' s)&lt;br /&gt;
    suf = {0, 1 .. s.length}&lt;br /&gt;
    '''sort'''(suf, compare1)&lt;br /&gt;
    c = {s[0], s[1] .. s[s.length - 1]}&lt;br /&gt;
 &lt;br /&gt;
    '''for''' l = 1 .. 2^('''ceil'''('''log2'''(n)) - 1) '''step''' l *= 2&lt;br /&gt;
        '''sort'''(suf, compare2)&lt;br /&gt;
        c'[suf[0]] = 0&lt;br /&gt;
        '''for''' i =  1 .. s.length - 1&lt;br /&gt;
            l1 = suf[i - 1]&lt;br /&gt;
            r1 = suf[i - 1] + l&lt;br /&gt;
            l2 = suf[i]&lt;br /&gt;
            r2 = suf[i] + l&lt;br /&gt;
            '''if''' c[l1] &amp;lt;tex&amp;gt;\neq&amp;lt;/tex&amp;gt; c[l2] '''or''' c[r1] &amp;lt;tex&amp;gt;\neq&amp;lt;/tex&amp;gt; c[r2]&lt;br /&gt;
                c'[suf[i]] = c'[suf[i - 1]] + 1&lt;br /&gt;
            '''else'''&lt;br /&gt;
                c'[suf[i]] = c'[suf[i - 1]]&lt;br /&gt;
         c = c'&lt;br /&gt;
    '''return''' suf&lt;br /&gt;
 &lt;br /&gt;
 '''order''' compare1('''int''' j1, '''int''' j2)&lt;br /&gt;
     '''if''' s[j1] &amp;lt; s[j2]&lt;br /&gt;
         '''return''' ''LT''&lt;br /&gt;
     '''else if''' s[j1] == s[j2]&lt;br /&gt;
         '''return''' ''EQ''&lt;br /&gt;
     '''else'''&lt;br /&gt;
         '''return''' ''GT''&lt;br /&gt;
 &lt;br /&gt;
'''// Почему compare2 использует compare1, который сравнивает лишь символы, а не классы эквивалентности?'''&lt;br /&gt;
 '''order''' compare2('''int''' j1, '''int''' j2)&lt;br /&gt;
     '''if''' c[j1] &amp;lt;tex&amp;gt;\neq&amp;lt;/tex&amp;gt; c[j2]&lt;br /&gt;
         '''return''' '''compare1'''(j1, j2)&lt;br /&gt;
     '''else'''&lt;br /&gt;
         '''return''' '''compare1'''(j1 + l, j2 + l)&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
* [[Суффиксный массив]]&lt;br /&gt;
* [[Алгоритм цифровой сортировки суффиксов циклической строки]]&lt;br /&gt;
&lt;br /&gt;
==Источники информации==&lt;br /&gt;
[http://en.wikipedia.org/wiki/Suffix_array#Construction_Algorithms Wikipedia — Suffix array construction algorithms]&lt;br /&gt;
&lt;br /&gt;
[[Категория:Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория:Суффиксный массив]]&lt;/div&gt;</summary>
		<author><name>100.12.169.25</name></author>	</entry>

	</feed>