Декомпозиция Линдона — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
Строка 1: Строка 1:
Декомпозиция Линдона была изобретена Роджером Линдоном (англ. ''Roger Lyndon'') в 1954 году. Она используется для нахождения лексикографически минимального и максимального суффиксов строки.
+
Декомпозиция Линдона была изобретена Роджером Линдоном (англ. ''Roger Lyndon'') в 1954 году. Она используется для нахождения лексикографически минимального и максимального суффиксов строки, а также лексикографически минимального циклического сдвига.
  
 
==Основные определения==
 
==Основные определения==
Строка 28: Строка 28:
 
2. <tex>s + t</tex> {{---}} простая  
 
2. <tex>s + t</tex> {{---}} простая  
 
|proof=
 
|proof=
1. Так как <tex>s < t</tex>, <tex>\mathcal {9} i : s[i] < t[i]</tex> и <tex>s[j] = t[j]</tex>, <tex>j < i \rightarrow s + t < t</tex>
+
1. Так как <tex>s < t</tex>, то <tex>\exists i : s[i] < t[i]</tex> и <tex>s[j] = t[j]</tex>, <tex>j < i \Rightarrow s + t < t</tex>
  
2. Пусть <tex>u</tex> {{---}} суффикс строки <tex>s + t</tex>
+
2. Пусть <tex>u</tex> {{---}} суффикс строки <tex>s + t</tex>. Тогда рассмотрим <tex> 3 </tex> возможных случая:
  
1) <tex>|u| = |t| \rightarrow u = t \rightarrow u > s + t</tex> по пункту 1.
+
* <tex>|u| = |t| \Rightarrow u = t \Rightarrow u > s + t</tex> по пункту <tex> 1 </tex>.
 
+
* <tex>|u| < |t| \Rightarrow u</tex> {{---}} суффикс <tex>t</tex>. Так как <tex>t</tex> {{---}} простая, и <tex>t < u </tex> по определению <tex> \Rightarrow s + t < t < u</tex>
2) <tex>|u| < |t| \rightarrow u</tex> {{---}} суффикс <tex>t</tex>. Так как <tex>t</tex> {{---}} простая, <tex>t < u по определению \rightarrow s + t < t < u</tex>
+
* <tex>|u| > |t| \Rightarrow s = s' + s''</tex>, <tex>u = s'' + t</tex>. Так как <tex>s</tex> {{---}} простая, <tex>s < s''</tex> и <tex>|s''| < |s| \Rightarrow s + t < s'' + t</tex>
 
 
3) <tex>|u| > |t| \rightarrow s = s' + s''</tex>, <tex>u = s'' + t</tex>. Так как <tex>s</tex> {{---}} простая, <tex>s < s''</tex> и <tex>|s''| < |s| \rightarrow s + t < s'' + t</tex>
 
 
}}
 
}}
  
 
{{Теорема
 
{{Теорема
 
|id=theorem
 
|id=theorem
 +
|author=Чен-Линдон-Фокс
 
|statement=Можно построить декомпозицию Линдона любой строки <tex>s</tex>, причем единственным образом.
 
|statement=Можно построить декомпозицию Линдона любой строки <tex>s</tex>, причем единственным образом.
 
|proof=  
 
|proof=  
 
1. Существование.
 
1. Существование.
  
Разобьем строку на символы. Будем их склеивать, если подряд идущие символы: <tex>s[i] < s[i+1]</tex>. Так как символ {{---}} простая строка, по лемме <tex>s[i..i+1]</tex> {{---}} тоже простая и <tex>s[i..i+1] < s[i]</tex>.
+
У каждой строки существует хотя бы одно разбиение на простые слова. Это следует из того, что отдельный символ является простым словом. Тогда среди всех разбиений строки на простые слова возьмём то, в котором меньше всего слов. Покажем, что это и будет декомпозицией Линдона данной строки.
Далее склеиваем строки, не удовлетворяющие условию <tex>s_1 \geqslant s_2 \geqslant ... \geqslant s_k</tex>.
 
Это конечный процесс, так как длина строки конечна <tex>\rightarrow</tex> получим нужное разбиение.
 
  
Пусть существует хотя бы одно разбиение строки на простые слова.
+
Предположим, что это не так. Значит, <tex>\exists i : s_i < s_{i+1}</tex>. Так как слова <tex> s_i </tex> и <tex> s_{i+1} </tex> простые, то из доказанной [[Декомпозиция Линдона#lemma | леммы]] следует, что эти слова можно сконкатенировать и получить разбиение строки <tex> s </tex> на меньшее число слов. Получили противоречие.
Возьмем разбиение строки на простые слова (без условия  <tex>s_1 \geqslant s_2 \geqslant ... \geqslant s_k</tex>) такое, чтобы <tex>k</tex> было минимально.
 
Пусть в нем есть <tex>s_i < s_{i+1}</tex>, тогда эти строки можно сконкатернировать <tex>\rightarrow</tex> получим разбиение с меньшим числом слов {{---}} противоречие с выбором <tex>k</tex>.
 
  
Получили: <tex>k</tex> {{---}} минимально <tex>\Leftrightarrow</tex> нет <tex>s_i < s_{i+1}</tex>
+
Таким образом доказали даже более сильное утверждение: <tex>s = s_1 s_2 ... s_k</tex>, <tex> k </tex> {{---}} минимально <tex>\Leftrightarrow</tex> нет <tex>s_i < s_{i+1}</tex>
  
 
2. Единственность.
 
2. Единственность.
Строка 61: Строка 56:
 
Сравним длины первых двух слов <tex>s_1</tex> и <tex>s_1'</tex>, если <tex>|s_1| = |s_1'|</tex>, сравним вторые и так далее.
 
Сравним длины первых двух слов <tex>s_1</tex> и <tex>s_1'</tex>, если <tex>|s_1| = |s_1'|</tex>, сравним вторые и так далее.
 
Если у всех слов длины одинаковы, то разбиения совпадают {{---}} противоречие.
 
Если у всех слов длины одинаковы, то разбиения совпадают {{---}} противоречие.
Иначе <tex>\mathcal {9} s_i : |s_i| \neq |s_i'|</tex>
+
Иначе <tex>\exists s_i : |s_i| \neq |s_i'|</tex>.
 +
 
 
Покажем, что такого не может быть:
 
Покажем, что такого не может быть:
  
1) Пусть <tex>|s_i| > |s_i'|</tex>
+
1) Пусть <tex>|s_i| > |s_i'|</tex>, тогда <tex>s_i = s_i's_{i+1}'...t</tex>, где <tex>t</tex> {{---}} префикс <tex>s_{j+1}'</tex>, <tex>i < j</tex>.
Тогда <tex>s_i = s_i's_{i+1}'...t</tex>, где <tex>t</tex> {{---}} префикс <tex>s_{j+1}'</tex>, <tex>i \leqslant j</tex>
+
 
Получаем: <tex>s_i < t</tex> (так как <tex>s_i</tex> простая и по определению меньше своего суффикса),
+
Получаем: <tex>s_i < t</tex>, так как <tex>s_i</tex> простая и по определению меньше своего суффикса,
<tex>t < s_{j+1}'</tex> (так как <tex>t</tex> {{---}} префикс),
+
<tex>t < s_{j+1}'</tex> (так как <tex>t</tex> {{---}} префикс ???),
 
<tex>s_{j+1}' < s_i'</tex> (по условию разбиения),
 
<tex>s_{j+1}' < s_i'</tex> (по условию разбиения),
 
<tex>s_i' < s_i</tex> (их начало совпадает, и <tex>|s_i| < |s_i'|</tex> по предположению.
 
<tex>s_i' < s_i</tex> (их начало совпадает, и <tex>|s_i| < |s_i'|</tex> по предположению.
 
Получили противоречие: <tex>s_i < s_i</tex>.
 
Получили противоречие: <tex>s_i < s_i</tex>.
  
2) Пусть <tex>|s_i| < |s_i'|</tex> {{---}} проверяется аналогично.
+
2) Случай <tex>|s_i| < |s_i'|</tex> симметричен разобранному.
То есть не может быть строк <tex>s_i</tex> несовпадающей длины <tex>\rightarrow</tex> разбиения равны.
+
 
 +
То есть не может быть строк <tex>s_i</tex> несовпадающей длины, значит, разбиения равны.
 
}}
 
}}
  
Строка 124: Строка 121:
 
Дополнительная память требуется только на три указателя: <tex>i, j, k</tex>.
 
Дополнительная память требуется только на три указателя: <tex>i, j, k</tex>.
  
Внешний цикл <tex>while</tex> делает не более <tex>n</tex> итераций, поскольку в конце каждой его итерации к результату добавляется как минимум один символ (а всего символов <tex>n</tex>).
+
Внешний цикл <tex>\mathrm{while}</tex> делает не более <tex>n</tex> итераций, поскольку в конце каждой его итерации к результату добавляется как минимум один символ (а всего символов <tex>n</tex>).
  
Оценим теперь количество итераций первого вложенного цикла <tex>while</tex>. Для этого рассмотрим второй вложенный цикл <tex>while</tex> {{---}} он при каждом своём запуске выводит некоторое количество <tex>r \geqslant 1</tex> копий одной и той же простой строки некоторой длины <tex>p = j - k</tex>. Заметим, что строка <tex>s_2</tex> является предпростой, причём её простые строки имеют длину как раз <tex>p</tex>, т.е. её длина не превосходит <tex>r \cdot p + p - 1</tex>. Поскольку длина строки <tex>s_2</tex> равна <tex>j - i</tex>, а указатель <tex>j</tex> увеличивается по единице на каждой итерации первого вложенного цикла <tex>while</tex>, то этот цикл выполнит не более <tex>r \cdot p + p - 2</tex> итераций. Худшим случаем является случай <tex>r = 1</tex>, и мы получаем, что первый вложенный цикл <tex>while</tex> всякий раз выполняет не более <tex>2p - 2</tex> итераций. Вспоминая, что всего выводится <tex>n</tex> символов, получаем, что для вывода <tex>n</tex> символов требуется не более <tex>2n - 2</tex> итераций первого вложенного <tex>while</tex>. Следовательно, алгоритм Дюваля выполняется за <tex>O(n)</tex>.
+
Оценим теперь количество итераций первого вложенного цикла <tex>\mathrm{while}</tex>. Для этого рассмотрим второй вложенный цикл <tex>\mathrm{while}</tex> {{---}} он при каждом своём запуске выводит некоторое количество <tex>r \geqslant 1</tex> копий одной и той же простой строки некоторой длины <tex>p = j - k</tex>. Заметим, что строка <tex>s_2</tex> является предпростой, причём её простые строки имеют длину как раз <tex>p</tex>, т.е. её длина не превосходит <tex>r \cdot p + p - 1</tex>. Поскольку длина строки <tex>s_2</tex> равна <tex>j - i</tex>, а указатель <tex>j</tex> увеличивается по единице на каждой итерации первого вложенного цикла <tex>\mathrm{while}</tex>, то этот цикл выполнит не более <tex>r \cdot p + p - 2</tex> итераций. Худшим случаем является случай <tex>r = 1</tex>, и мы получаем, что первый вложенный цикл <tex>\mathrm{while}</tex> всякий раз выполняет не более <tex>2p - 2</tex> итераций. Вспоминая, что всего выводится <tex>n</tex> символов, получаем, что для вывода <tex>n</tex> символов требуется не более <tex>2n - 2</tex> итераций первого вложенного <tex>\mathrm{while}</tex>. Следовательно, алгоритм Дюваля выполняется за <tex>O(n)</tex>.
  
Легко оценить и число сравнений символов, выполняемых алгоритмом Дюваля. Поскольку каждая итерация первого вложенного цикла <tex>while</tex> производит два сравнения символов, а также одно сравнение производится после последней итерации цикла, то общее число сравнений символов не превосходит <tex>4n - 3</tex>.
+
Легко оценить и число сравнений символов, выполняемых алгоритмом Дюваля. Поскольку каждая итерация первого вложенного цикла <tex>\mathrm{while}</tex> производит два сравнения символов, а также одно сравнение производится после последней итерации цикла, то общее число сравнений символов не превосходит <tex>4n - 3</tex>.

Версия 12:38, 3 мая 2014

Декомпозиция Линдона была изобретена Роджером Линдоном (англ. Roger Lyndon) в 1954 году. Она используется для нахождения лексикографически минимального и максимального суффиксов строки, а также лексикографически минимального циклического сдвига.

Основные определения

Определение:
Простая строка — строка, которая лексикографически меньше любого своего суффикса.


Примеры:

[math]ababb[/math] — простая строка, так как [math]ababb \lt babb[/math], [math]ababb \lt abb[/math], [math]ababb \lt bb[/math], [math]ababb \lt b[/math].

[math]babaa[/math] — не простая строка, так как [math]babaa \gt aa[/math].


Определение:
Декомпозиция Линдона (англ. Lyndon decomposition) строки [math]s[/math] — её разложение [math]s = s_1s_2...s_k[/math], где строки [math]s_i[/math] просты, и при этом [math]s_1 \geqslant s_2 \geqslant ... \geqslant s_k[/math].


Существование и единственность

Лемма:
[math]s [/math], [math]t[/math] — простые и [math]s \lt t[/math] лексикографически. Тогда верны следующие утверждения:

1. [math]s + t \lt t[/math]

2. [math]s + t[/math] — простая
Доказательство:
[math]\triangleright[/math]

1. Так как [math]s \lt t[/math], то [math]\exists i : s[i] \lt t[i][/math] и [math]s[j] = t[j][/math], [math]j \lt i \Rightarrow s + t \lt t[/math]

2. Пусть [math]u[/math] — суффикс строки [math]s + t[/math]. Тогда рассмотрим [math] 3 [/math] возможных случая:

  • [math]|u| = |t| \Rightarrow u = t \Rightarrow u \gt s + t[/math] по пункту [math] 1 [/math].
  • [math]|u| \lt |t| \Rightarrow u[/math] — суффикс [math]t[/math]. Так как [math]t[/math] — простая, и [math]t \lt u [/math] по определению [math] \Rightarrow s + t \lt t \lt u[/math]
  • [math]|u| \gt |t| \Rightarrow s = s' + s''[/math], [math]u = s'' + t[/math]. Так как [math]s[/math] — простая, [math]s \lt s''[/math] и [math]|s''| \lt |s| \Rightarrow s + t \lt s'' + t[/math]
[math]\triangleleft[/math]
Теорема (Чен-Линдон-Фокс):
Можно построить декомпозицию Линдона любой строки [math]s[/math], причем единственным образом.
Доказательство:
[math]\triangleright[/math]

1. Существование.

У каждой строки существует хотя бы одно разбиение на простые слова. Это следует из того, что отдельный символ является простым словом. Тогда среди всех разбиений строки на простые слова возьмём то, в котором меньше всего слов. Покажем, что это и будет декомпозицией Линдона данной строки.

Предположим, что это не так. Значит, [math]\exists i : s_i \lt s_{i+1}[/math]. Так как слова [math] s_i [/math] и [math] s_{i+1} [/math] простые, то из доказанной леммы следует, что эти слова можно сконкатенировать и получить разбиение строки [math] s [/math] на меньшее число слов. Получили противоречие.

Таким образом доказали даже более сильное утверждение: [math]s = s_1 s_2 ... s_k[/math], [math] k [/math] — минимально [math]\Leftrightarrow[/math] нет [math]s_i \lt s_{i+1}[/math]

2. Единственность.

Пусть существует несколько разбиений [math]s = s_1s_2...s_k = s_1's_2'...s_k'[/math], удовлетворяющих условию теоремы. Сравним длины первых двух слов [math]s_1[/math] и [math]s_1'[/math], если [math]|s_1| = |s_1'|[/math], сравним вторые и так далее. Если у всех слов длины одинаковы, то разбиения совпадают — противоречие. Иначе [math]\exists s_i : |s_i| \neq |s_i'|[/math].

Покажем, что такого не может быть:

1) Пусть [math]|s_i| \gt |s_i'|[/math], тогда [math]s_i = s_i's_{i+1}'...t[/math], где [math]t[/math] — префикс [math]s_{j+1}'[/math], [math]i \lt j[/math].

Получаем: [math]s_i \lt t[/math], так как [math]s_i[/math] простая и по определению меньше своего суффикса, [math]t \lt s_{j+1}'[/math] (так как [math]t[/math] — префикс ???), [math]s_{j+1}' \lt s_i'[/math] (по условию разбиения), [math]s_i' \lt s_i[/math] (их начало совпадает, и [math]|s_i| \lt |s_i'|[/math] по предположению. Получили противоречие: [math]s_i \lt s_i[/math].

2) Случай [math]|s_i| \lt |s_i'|[/math] симметричен разобранному.

То есть не может быть строк [math]s_i[/math] несовпадающей длины, значит, разбиения равны.
[math]\triangleleft[/math]

Алгоритм Дюваля

Алгоритм

Алгоритм Дюваля (англ. Duval's algorithm) находит для данной строки длины [math]n[/math] декомпозицию Линдона за время [math]O(n)[/math] с использованием [math]O(1)[/math] дополнительной памяти. Он строит декомпозицию только на упорядоченных алфавитах. Алгоритм Дюваля относится к классу жадных алгоритмов.


Определение:
Предпростая строка — строка [math]s[/math], такая что [math]s = ww...ww'[/math], где [math]w[/math] — некоторая простая строка, а [math]w'[/math] — некоторый префикс строки [math]w[/math].


Во время работы алгоритма строка [math]s[/math] разделена на три строки [math]s = s_1s_2s_3[/math], где в строке [math]s_1[/math] декомпозиция Линдона уже найдена и [math]s_1[/math] уже больше не используется алгоритмом; строка [math]s_2[/math] — это предпростая строка (причём длину простых строк внутри неё мы также запоминаем); строка [math]s_3[/math] — это ещё не обработанная часть строки [math]s[/math]. Алгоритм Дюваля берёт первый символ строки [math]s_3[/math] и пытается дописать его к строке [math]s_2[/math]. При этом, возможно, для какого-то префикса строки [math]s_2[/math] декомпозиция Линдона становится известной, и эта часть переходит к строке [math]s_1[/math].

Будем поддерживаться указатель [math]i[/math] на начало строки [math]s_2[/math]. Внешний цикл алгоритма будет выполняться, пока [math]i \lt n[/math], то есть пока вся строка [math]s[/math] не перейдёт в строку [math]s_1[/math]. Внутри этого цикла создаются два указателя: указатель [math]j[/math] на начало строки [math]s_3[/math] и указатель [math]k[/math] на текущий символ в строке [math]s_2[/math], с которым будет производиться сравнение. Затем будем в цикле пытаться добавить символ [math]s[j][/math] к строке [math]s_2[/math], для чего необходимо произвести сравнение с символом [math]s[k][/math]. Здесь у нас возникают три различных случая:

1. Если [math]s[j] = s[k][/math], то мы можем дописать символ [math]s[j][/math] к строке [math]s_2[/math], не нарушив её "предпростоты". Следовательно, в этом случае мы просто увеличиваем указатели [math]j[/math] и [math]k[/math] на единицу.

2. Если [math]s[j] \gt s[k][/math], то, очевидно, строка [math]s_2 + s[j][/math] станет простой. Тогда мы увеличиваем [math]j[/math] на единицу, а [math]k[/math] передвигаем обратно на [math]i[/math], чтобы следующий символ сравнивался с первым символом [math]s_2[/math].

3. Если [math]s[j] \lt s[k][/math], то строка [math]s_2 + s[j][/math] уже не может быть предпростой. Поэтому мы разбиваем предпростую строку [math]s_2[/math] на простые строки плюс "остаток" (префикс простой строки, возможно, пустой); простые строки добавляем в ответ (т.е. выводим их позиции, попутно передвигая указатель [math]i[/math]), а "остаток" вместе с символом [math]s[j][/math] переводим обратно в строку [math]s_3[/math], и останавливаем выполнение внутреннего цикла. Тем самым мы на следующей итерации внешнего цикла заново обработаем остаток, зная, что он не мог образовать предпростую строку с предыдущими простыми строками. Осталось только заметить, что при выводе позиций простых строк нам нужно знать их длину; но она, очевидно, равна [math]j - k[/math].

Реализация

   string s // входная строка
   string[] words // декомпозиция
   n [math]\leftarrow[/math] |s|
   i [math]\leftarrow[/math] 0
   w [math]\leftarrow[/math] 0
   while i < n {
   	j [math]\leftarrow[/math] i + 1
       k [math]\leftarrow[/math] i
   	while j < n and s[k] <= s[j] {
   	    if s[k] < s[j]
               k [math]\leftarrow[/math] i
           else
               k [math]\leftarrow[/math] k + 1
           j [math]\leftarrow[/math] j + 1
       }
       while i [math]\leqslant[/math] k {
           words[w] [math]\leftarrow[/math] s[i..j-k]
           w [math]\leftarrow[/math] w + 1
           i [math]\leftarrow[/math] i + j - k;
       }
   }

Корректность

Асимптотика

Дополнительная память требуется только на три указателя: [math]i, j, k[/math].

Внешний цикл [math]\mathrm{while}[/math] делает не более [math]n[/math] итераций, поскольку в конце каждой его итерации к результату добавляется как минимум один символ (а всего символов [math]n[/math]).

Оценим теперь количество итераций первого вложенного цикла [math]\mathrm{while}[/math]. Для этого рассмотрим второй вложенный цикл [math]\mathrm{while}[/math] — он при каждом своём запуске выводит некоторое количество [math]r \geqslant 1[/math] копий одной и той же простой строки некоторой длины [math]p = j - k[/math]. Заметим, что строка [math]s_2[/math] является предпростой, причём её простые строки имеют длину как раз [math]p[/math], т.е. её длина не превосходит [math]r \cdot p + p - 1[/math]. Поскольку длина строки [math]s_2[/math] равна [math]j - i[/math], а указатель [math]j[/math] увеличивается по единице на каждой итерации первого вложенного цикла [math]\mathrm{while}[/math], то этот цикл выполнит не более [math]r \cdot p + p - 2[/math] итераций. Худшим случаем является случай [math]r = 1[/math], и мы получаем, что первый вложенный цикл [math]\mathrm{while}[/math] всякий раз выполняет не более [math]2p - 2[/math] итераций. Вспоминая, что всего выводится [math]n[/math] символов, получаем, что для вывода [math]n[/math] символов требуется не более [math]2n - 2[/math] итераций первого вложенного [math]\mathrm{while}[/math]. Следовательно, алгоритм Дюваля выполняется за [math]O(n)[/math].

Легко оценить и число сравнений символов, выполняемых алгоритмом Дюваля. Поскольку каждая итерация первого вложенного цикла [math]\mathrm{while}[/math] производит два сравнения символов, а также одно сравнение производится после последней итерации цикла, то общее число сравнений символов не превосходит [math]4n - 3[/math].