Декомпозиция Линдона — различия между версиями
Shersh (обсуждение | вклад) (→Алгоритм) |
Shersh (обсуждение | вклад) (→Корректность) |
||
| Строка 121: | Строка 121: | ||
Во втором случае объединяем все найденные <tex>w</tex> с <tex>w'</tex> и получем новую строку <tex>w''</tex>. | Во втором случае объединяем все найденные <tex>w</tex> с <tex>w'</tex> и получем новую строку <tex>w''</tex>. | ||
| − | Покажем, что <tex>w''</tex> является простой. Рассмотрим ее суффикс. Если он начинается в середине <tex>w</tex>, сравним его посимвольно со строкой <tex>s_2</tex>, и тогда в каком-то символе он окажется больше <tex>s_2</tex>, так как суффикс <tex> w'' </tex> начинается с <tex> u </tex> {{---}} суффикса <tex>w</tex>, а строка <tex>w</tex> {{---}} простая и по определению меньше всех своих суффиксов. Если суффикс начинается в <tex>w'</tex>, то при сравнении расхождение будет в символах <tex>s[j]</tex> и <tex>s[k]</tex>. Но <tex>s[j] < s[k]</tex>, так что суффикс больше <tex>w''</tex>. Если же суффикс начинается с первой позиции какой-то подстроки <tex>w</tex>, то отбросим общий префикс вида <tex> | + | Покажем, что <tex>w''</tex> является простой. Рассмотрим ее суффикс. Если он начинается в середине <tex>w</tex>, сравним его посимвольно со строкой <tex>s_2</tex>, и тогда в каком-то символе он окажется больше <tex>s_2</tex>, так как суффикс <tex> w'' </tex> начинается с <tex> u </tex> {{---}} суффикса <tex>w</tex>, а строка <tex>w</tex> {{---}} простая и по определению меньше всех своих суффиксов. Если суффикс начинается в <tex>w'</tex>, то при сравнении расхождение будет в символах <tex>s[j]</tex> и <tex>s[k]</tex>. Но <tex>s[j] < s[k]</tex>, так что суффикс больше <tex>w''</tex>. Если же суффикс начинается с первой позиции какой-то подстроки <tex>w</tex>, то отбросим общий префикс вида <tex>ww \dots w</tex> и придем к предыдущему случаю. |
В третьем случае просто выведем все <tex>w</tex> и продолжим обработку со строки <tex>w'</tex>, так как при добавлении <tex>s[k] </tex>, <tex>s_2</tex> перестанет удовлетворять требованиям, ведь в этом случае суффикс строки <tex> s_2 </tex> равный <tex> w'</tex> будет меньше <tex>w</tex>. | В третьем случае просто выведем все <tex>w</tex> и продолжим обработку со строки <tex>w'</tex>, так как при добавлении <tex>s[k] </tex>, <tex>s_2</tex> перестанет удовлетворять требованиям, ведь в этом случае суффикс строки <tex> s_2 </tex> равный <tex> w'</tex> будет меньше <tex>w</tex>. | ||
Версия 13:08, 6 мая 2014
Декомпозиция Линдона была изобретена Роджером Линдоном (англ. Roger Lyndon) в 1954 году. Она используется для нахождения лексикографически минимального и максимального суффиксов строки, а также лексикографически минимального циклического сдвига.
Содержание
Основные определения
| Определение: |
| Простая строка — строка, которая лексикографически меньше любого своего суффикса. |
Примеры:
— простая строка, так как , , , .
— не простая строка, так как .
| Определение: |
| Декомпозиция Линдона (англ. Lyndon decomposition) строки — её разложение , где строки просты, и при этом . |
Существование и единственность
| Лемма: |
, — простые и лексикографически. Тогда верны следующие утверждения:
1. 2. — простая |
| Доказательство: |
|
1. Так как , то и , 2. Пусть — суффикс строки . Тогда рассмотрим 3 возможных случая:
|
| Теорема (Чен-Линдон-Фокс): |
Можно построить декомпозицию Линдона любой строки , причем единственным образом. |
| Доказательство: |
|
1. Существование. У каждой строки существует хотя бы одно разбиение на простые слова. Это следует из того, что отдельный символ является простым словом. Тогда среди всех разбиений строки на простые слова возьмём то, в котором меньше всего слов. Покажем, что это и будет декомпозицией Линдона данной строки. Предположим, что это не так. Значит, . Так как слова и простые, то из доказанной леммы следует, что эти слова можно сконкатенировать и получить разбиение строки на меньшее число слов. Получили противоречие. Таким образом доказали даже более сильное утверждение: , — минимально нет 2. Единственность. Пусть существует несколько разбиений , удовлетворяющих условию теоремы. Сравним длины первых двух слов и , если , сравним вторые и так далее. Если длины всех слов одинаковы, то разбиения совпадают — противоречие. Иначе . Покажем, что такого не может быть: 1) Пусть , тогда , где — префикс , . Тогда получаем:
Пришли к противоречию: . 2) Случай симметричен разобранному. То есть не может быть строк и несовпадающей длины, значит, разбиения равны. |
Алгоритм Дюваля
Алгоритм
Алгоритм Дюваля (англ. Duval's algorithm) находит для данной строки длины декомпозицию Линдона за время с использованием дополнительной памяти. Он строит декомпозицию только на упорядоченных алфавитах.
| Определение: |
| Предпростая строка — строка , такая что , где — некоторая простая строка, а — некоторый префикс строки . |
Во время работы алгоритма строка представляется в виде конкатенации трёх строк , где для строки декомпозиция Линдона уже найдена, и уже больше не используется алгоритмом; строка — это предпростая строка; строка — ещё не обработанная алгоритмом часть строки . Алгоритм Дюваля берёт первый символ строки и пытается дописать его к строке . При этом, возможно, для какого-то префикса строки декомпозиция Линдона становится известной, и эта часть переходит к строке .
Будем поддерживать три указателя:
- — на начало строки
- — на текущий символ в строке , с которым будет производиться сравнение
- — на начало строки
Внешний цикл алгоритма будет выполняться, пока , то есть пока вся строка не перейдёт в строку . Внутри этого цикла создаются два указателя и . Затем будем пытаться добавить символ к строке , для чего необходимо произвести сравнение с символом . При этом будем поддерживать инвариант: — длина подстроки .
Возникают три различных случая:
- тогда дописывыем символ к строке и увеличиваем оба указателя на единицу.
- тогда строка станет простой. Значит, мы увеличим на единицу, а передвигаем обратно на , чтобы следующий символ сравнивался с первым символом . То есть получаем новую простую строку длины .
- значит, строка уже не может быть предпростой. Добавляем к все строки , а по нашему инварианту мы знаем, что их длина равна , затем сдвигаем к началу позиции строки . После чего внешний цикл запускаем заново:
Реализация
function lyndon(string s, string[] decomposition): n |s| i 0 cur 0 while i n: j i k i + 1 while k n and s[j] s[k]: if s[j] s[k]: j i else: j k + 1 k k + 1 while i j: decomposition[cur] s[i..k - j] cur cur + 1 i i + k - j
Корректность
Покажем, что алгоритм получает нужное разложение. То есть все — простые, и лексикографически.
При обработке текущего символа в первом случае просто сдвигаем указатели, не записывая ответ. Мы сравниваем символы в и на одинаковых позициях, а — префикс , поэтому инвариант сохраняется.
Во втором случае объединяем все найденные с и получем новую строку .
Покажем, что является простой. Рассмотрим ее суффикс. Если он начинается в середине , сравним его посимвольно со строкой , и тогда в каком-то символе он окажется больше , так как суффикс начинается с — суффикса , а строка — простая и по определению меньше всех своих суффиксов. Если суффикс начинается в , то при сравнении расхождение будет в символах и . Но , так что суффикс больше . Если же суффикс начинается с первой позиции какой-то подстроки , то отбросим общий префикс вида и придем к предыдущему случаю.
В третьем случае просто выведем все и продолжим обработку со строки , так как при добавлении , перестанет удовлетворять требованиям, ведь в этом случае суффикс строки равный будет меньше .
Теперь покажем, что .
Последоваельность из будет удовлетворять условию, так как эти строки равны. Следующее слово будет иметь общий префикс с , а после него будет стоять символ, меньший следующего символа из (новое получается по третьему случаю).
Асимптотика
Внешний цикл делает не более итераций, поскольку в конце каждой его итерации увеличивается как минимум на . Второй внутренний цикл выполнится суммарно не более , так он добавляет к ответу все символы, причём каждый символ лишь единожды.
Оценим теперь количество итераций первого вложенного цикла . Для этого рассмотрим второй вложенный цикл — он при каждом своём запуске выводит некоторое количество копий одной и той же простой строки некоторой длины . Заметим, что строка является предпростой, причём её простые строки имеют длину как раз , т.е. её длина не превосходит . Поскольку длина строки равна , а указатель увеличивается на единицу на каждой итерации первого вложенного цикла , то этот цикл выполнит не более итераций. Худшим случаем является случай , и мы получаем, что первый вложенный цикл всякий раз выполняет не более итераций. Вспоминая, что всего выводится символов, получаем, что для вывода символов требуется не более итераций первого вложенного .
Итого получаем, что итоговая асимптотика алгоритма составляет .
Отметим, что алгоритму требуется памяти: на указатели .