Декомпозиция Линдона — различия между версиями
(→Алгоритм) |
(→Построение искомой структуры данных) |
||
| Строка 176: | Строка 176: | ||
===Построение искомой структуры данных=== | ===Построение искомой структуры данных=== | ||
| + | Простой алгоритм построения с временем работы <tex>\mathcal{O}(n\log n)</tex> также основывается на Лемме 1. Покажем, что построив улучшенный суфмассив, мы можем найти <tex>B_{j}</tex> за <tex>\mathcal{O}(\log n)</tex>. Мы ищем минимальный суффикс <tex>S_{j}^{\ell}</tex> для последовательных значений <tex>\ell</tex>. Как только мы получили результат <tex>\ell-1</tex>, первый случай Леммы 1 даёт нам второго кандидата на минимальный суффикс <tex>S_{j}^{\ell}</tex>, и наш улучшенный суфмассив позволяет нам выбрать наименьшего из этих двух кандидатов. Мы устанавливаем <tex>B_{j}[\ell]=1</tex> если меньший кандидат не содержится в <tex>S_{j}^{\ell-1}</tex>. Стало быть, мы получили следующий результат: | ||
| + | |||
| + | {{Теорема | ||
| + | |author=2 | ||
| + | |statement= | ||
| + | Строку <tex>T</tex> длины <tex>n</tex> можно уместить в структуру данных с <tex>\mathcal{O}(n)</tex> памяти, которая позволяет вычислять минимальный суффикс любой подстроки <tex>T</tex> за <tex>\mathcal{O}(1)</tex>. Эта структура данных может быть построена за <tex>\mathcal{O}(n\log n)</tex>. | ||
| + | }} | ||
| + | |||
| + | Вышеописанная конструкция проста и работает для любого выбора канонических подстрок, но, к сожалению, она не может быть использована для достижения компромисса между временем запроса и временем построения. Далее мы предложим особый способ выбора канонических подстрок и опишем альтернативный метод построения. Этот способ основывается на предположении, что по данной строке длины <tex>k</tex> мы можем найти минимальный суффикс для всех её префиксов за <tex>\mathcal{O}(k)</tex>. Следовательно, нам удобно иметь много <tex>S_{j}^{\ell}</tex>, которые являются префиксами друг друга. Тогда, естественным будет выбрать <tex>|S_{j}^{\ell}|=2^{\ell-1}+(j\ mod\ 2^{\ell-1})</tex> , поскольку все подстроки <tex>S_{\alpha 2^{l-1}}^{\ell},\ S_{\alpha 2^{l-1}+1}^{\ell},\ \ldots,\ S_{(\alpha+1)2^{l-1}-1}^{\ell}</tex> являются префиксами <tex>S_{(\alpha+1)2^{l-1}-1}^{\ell}</tex>. К сожалению, подстроки, выбранные таким способом, не удовлетворяют условию <tex>|S_{j}^{\ell}|\leq 2|S_{j}^{\ell-1}|</tex>, и, посему, нам необходимо немного изменить его. | ||
| + | |||
| + | Для <tex>\ell=1</tex> мы определим <tex>S_{j}^{1}=T[j..j]</tex>. Для <tex>\ell>1</tex> установим <tex>m=\lfloor\ell/2\rfloor-1</tex> и определим <tex>S_{j}^{\ell}</tex> таким образом: | ||
| + | <tex> | ||
| + | |S_{j}^{\ell}|= | ||
| + | \begin{cases} | ||
| + | 2 \cdot 2^{m}+(j\ mod\ 2^{m}),& \ell \ mod \ 2 = 0\\ | ||
| + | 3 \cdot 2^{m}+(j\ mod\ 2^{m}),& \textup{otherwise} | ||
| + | \end{cases}</tex> | ||
| + | |||
| + | Заметим, что если <tex>2 \cdot 2^{m}\leq j<3\cdot 2^{m}</tex>, то <tex>T[1..j]=S_{j}^{2m+2}</tex>, в то время как, если <tex>3 \cdot 2^{m}\leq j<4\cdot 2^{m}</tex>, то <tex>T[1..j]=S_{j}^{2m+3}</tex>. Очевидно, что количество таких подстрок, заканчивающихся в <tex>j</tex> получается <tex>\mathcal{O}(\log n)</tex>. Докажем далее, что канонические подстроки, выбранные вышеуказанным способом, имеют необходимые свойства. | ||
| + | |||
| + | {{Теорема | ||
| + | |author=3 | ||
| + | |statement= | ||
| + | Для любого <tex>S_{j}^{\ell}</tex> и <tex>S_{j}^{\ell+1}</tex> при <tex>\ell\geq 1</tex> мы имеем <tex>|S_{j}^{\ell+1}|<2|S_{j}^{\ell}|</tex> | ||
| + | |proof= Для <tex>\ell=1</tex> неравенство, очевидно, выполняется. Рассмотрим <tex>\ell\geq 2</tex>. Обозначим через <tex>m</tex>, как и ранее, <tex>\lfloor\ell/2\rfloor-1</tex>. Если <tex>\ell</tex> чётно, то <tex>\ell+1</tex> нечётно и мы имеем | ||
| + | <tex>|S_{j}^{\ell+1}|=3\cdot 2^{m}+(j\ mod \ 2^{m})<4\cdot 2^{m}\leq 2\cdot(2\cdot 2^{m}+(j\ mod \ 2^{m}))=2|S_{j}^{\ell}|</tex>, в то время как, для нечётного <tex>\ell</tex> выполняется | ||
| + | <tex>|S_{j}^{\ell+1}|=2\cdot 2^{m+1}+(j\ mod \ 2^{m+1})<3\cdot 2^{m+1}\leq 2\cdot(3\cdot 2^{m}+(j\ mod \ 2^{m}))=2|S_{j}^{\ell}|</tex> | ||
| + | }} | ||
| + | |||
| + | {{Теорема | ||
| + | |author=4 | ||
| + | |statement= Для <tex>1\leq i<j\leq n</tex>, величина <tex>\alpha(i,\ j)</tex> может быть посчитана за константное время. | ||
| + | |proof= Положим <tex> m=\lfloor\log|T[i..j]|\rfloor</tex>. Заметим, что | ||
| + | |||
| + | <tex>|S_{j}^{2m-1}|=3\cdot 2^{m-2}+(j \ mod \ 2^{m-2})<2^{m}\leq|T[i..j]|</tex> | ||
| + | |||
| + | <tex>|S_{j}^{2m+2}|=2\cdot 2^{m}+(j \ mod \ 2^{m})\geq 2^{m+1}>|T[i..j]|</tex>. | ||
| + | |||
| + | Таким образом, <tex>\alpha(i,\ j)\in\{2m-1,2m,\ 2m+1\}</tex>, и мы можем за константное время проверить, какое из этих трёх значений корректно. | ||
| + | }} | ||
| + | |||
| + | После построения улучшенного суфмассива, мы установили все биты<tex>B_{j}[1]</tex> в 1. После этого, для каждого <tex>\ell>1</tex> мы посчитали минимальные суффиксы подстрок <tex>S_{j}^{\ell}</tex>, как указано далее. Зафиксируем <tex>\ell>1</tex> и разобьём <tex>T</tex> на куски размером <tex>2^{m}</tex>(где <tex>m=\lfloor\ell/2\rfloor-1</tex>) . Теперь каждый <tex>S_{j}^{\ell}</tex> является префиксом конкатенации максимум 4х таких кусков. Вспомним, что по данной строке можно посчитать длины минимальных суффиксов всех её префиксов за линейное время с помощью одной из вариаций алгоритма Дюваля (Алгоритм 3.1 in [5]). Разделим <tex>T</tex> на куски длиной <tex>2^{m}</tex>(где <tex>m=\lfloor\ell/2\rfloor-1</tex>) и запустим этот алгоритм для каждых четырёх (или менее, в конце) последовательных кусков. Это даст нам минимальные суффиксы <tex>S_{j}^{\ell}</tex> для всех <tex>1\leq j\leq n</tex>, за время <tex>\mathcal{O}(n)</tex>. Значение <tex>B_{j}[\ell]</tex> определено с помощью сравнения длины вычисленного минимального суффикса <tex>S_{j}^{\ell}</tex> с <tex>|S_{j}^{\ell-1}|</tex>. У нас <tex>\mathcal{O}(\log n)</tex> фаз алгоритма, что даёт нам время <tex>\mathcal{O}(n\log n)</tex> и <tex>\mathcal{O}(n)</tex> требуемой памяти. | ||
==Ссылки== | ==Ссылки== | ||
Версия 16:36, 11 июня 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.length 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..i + k - j - 1] cur cur + 1 i i + k - j
Корректность
Покажем, что алгоритм получает нужное разложение. То есть все — простые, и лексикографически.
При обработке текущего символа в первом случае просто сдвигаем указатели, не записывая ответ. Мы сравниваем символы в и на одинаковых позициях, а — префикс , поэтому инвариант сохраняется.
Во втором случае объединяем все найденные с и получем новую строку .
Покажем, что является простой. Рассмотрим ее суффикс. Если он начинается в середине , сравним его посимвольно со строкой , и тогда в каком-то символе он окажется больше , так как суффикс начинается с — суффикса , а строка — простая и по определению меньше всех своих суффиксов. Если суффикс начинается в , то при сравнении расхождение будет в символах и . Но , так что суффикс больше . Если же суффикс начинается с первой позиции какой-то подстроки , то отбросим общий префикс вида и придем к предыдущему случаю.
В третьем случае просто выведем все и продолжим обработку со строки , так как при добавлении , перестанет удовлетворять требованиям, ведь в этом случае суффикс строки равный будет меньше .
Теперь покажем, что .
Последоваельность из будет удовлетворять условию, так как эти строки равны. Следующее слово будет иметь общий префикс с , а после него будет стоять символ, меньший следующего символа из (новое получается по третьему случаю), либо следующее слово будет просто префиксом , и, как следствие, оно будет меньше лексикографически.
Асимптотика
Внешний цикл делает не более итераций, поскольку в конце каждой его итерации увеличивается как минимум на . Второй внутренний цикл выполнится суммарно не более , так он добавляет к ответу все символы, причём каждый символ лишь единожды.
Оценим теперь количество итераций первого вложенного цикла . Для этого рассмотрим второй вложенный цикл — он при каждом своём запуске выводит некоторое количество копий одной и той же простой строки некоторой длины . Заметим, что строка является предпростой, причём её простые строки имеют длину как раз , то есть её длина не превосходит . Поскольку длина строки равна , а указатель увеличивается на единицу на каждой итерации первого вложенного цикла , то этот цикл выполнит не более итераций. Худшим случаем является случай , и мы получаем, что первый вложенный цикл всякий раз выполняет не менее итераций. Вспоминая, что всего выводится символов, получаем, что для вывода символов требуется не более итераций первого вложенного .
Итого получаем, что итоговая асимптотика алгоритма составляет .
Отметим, что алгоритму требуется памяти: на указатели .
Поиск лексикографически минимального суффикса строки
Поиск лексикографически минимального и максимального суффиксов строки - вопрос, который часто поднимается при решении различных теоретических задач. С помощью классического алгоритма Дюваля эта задача решается за линейное время и константный размер дополнительной памяти.
Если заметить, что данная нам строка является подстрокой заранее данного текста длиной , то выполнив некоторый предподсчёт, мы можем получать значения максимального и минимального суффиксов определённой подстроки гораздо быстрее, чем линейно. Это может быть очень полезным при работе с большими объёмами данных (такими как генетический код и т.д.)
Покажем, что существует структура данных, размер которой линейно зависит от длины данного текста, со временем запроса и временем препроцессинга для запросов на нахождение минимального суффикса.
Будем обозначать и суффиксный массив и инвертированный суффиксный массив строки соответственно. Для данных индексов будем обозначать массив . SA и ISA могут быть улучшены за , чтобы отвечать на запросы вида
- по данным подстрокам и строки найти и определить, какая из подстрок лексикографически меньше
- по индексам и вычислить максимальный и минимальный суффикс в
Более того, такой улучшенный суффиксный массив может отвечать на запрос "по данным - подстрокам вычислить максимальное чило , такое, что является префиксом " за константное время. Действительно, стоит заметить, что если - префикс , то
Запросы к перевёрнутому улучшенному суфмассиву также имеют смысл. С его помощью мы можем для пары подстрок найти их наибольший общий суффикс и наибольшее число , такое, что является суффиксом .
Возьмём строку длины . Для каждой позиции мы выберем O(logN) подстрок , которые мы назовём каноническими. Определим как -ю кратчайшую каноническую подстроку, заканчивающуюся в позиции . Для пары целых чисел мы определим как наибольшее , такое, что - суффикс .
Мы потребуем, чтобы канонические подстроки удовлетворяли определённым условиям:
- и для некоторого выполняется
- и можно вычислить за константное время для данных и соответственно
Такая структура данных работает при любом выборе канонических подстрок, которые удовлетворяют вышеприведённым условиям, например при простейшем
| Лемма (1): |
Минимальный суффикс равен либо , где -начальная позиция минимального суффикса в , либо минимальному суффиксу . Более того, может быть найдено за константное время с использованием |
| Доказательство: |
| По Лемме 1 из 1 минимальный суффикс равен либо , либо его кратчайшему непустому бордеру. Более того, в последнем случае длина минимального суффикса равна не превышает . С другой стороны, по второму свойству канониеских подстрок, длина равна как минимум . Таким образом, во втором случае минимальный суффикс является минимальным суффиксом . Заметим, что для значения не определены, но тогда выполняется первый случай из условия леммы. Чтобы доказать финальное выражение, вспомним, что нахождение минимального суффикса - одна из базовых операций, поддерживаемых улучшенным суфмассивом. |
Требуемая структура данных, помимо улучшенного суфмассива, должна, для каждого содержать битовый вектор длиной . Положим тогда и только тогда, когда минимальный суффикс длиннее, чем . Для мы всегда считаем , поскольку является минимальным суффиксом самого себя. Вспомним, что количество канонических подстрок для каждого равна , поэтому каждый вмещается в константное количество машинных слов и структура данных занимает памяти.
Алгоритм запроса
Предположим, что мы ищем минимальный суффикс c . Наш подход основан на Лемме 1. Если выполняется её первый случай, лемма позволяет нам вычислить ответ за . В общем случае, мы найдём минимальный суффикс , сравним его с и вернём меньший из них.
Мы используем Лемму 1 и битовый вектор чтобы посчитать минимальный суффикс . Назовём наибольший индекс, не превышающий , такой, что . Заметим, что такой индекс всегда существует (поскольку) и может быть найден за константное время с использованием бтовых операций. Для любого индекса мы имеем , т.е., второй случай Леммы 1 выполняется для . Тогда, по индукции, минимальный суффикс на самом деле является минимальным суффиксом . С другой стороны, , поэтому для последнего мы можем гарантировать, что выполняется первый случай леммы, что позволяет нам найти минимальный суффикс за константное время.
Построение искомой структуры данных
Простой алгоритм построения с временем работы также основывается на Лемме 1. Покажем, что построив улучшенный суфмассив, мы можем найти за . Мы ищем минимальный суффикс для последовательных значений . Как только мы получили результат , первый случай Леммы 1 даёт нам второго кандидата на минимальный суффикс , и наш улучшенный суфмассив позволяет нам выбрать наименьшего из этих двух кандидатов. Мы устанавливаем если меньший кандидат не содержится в . Стало быть, мы получили следующий результат:
| Теорема (2): |
Строку длины можно уместить в структуру данных с памяти, которая позволяет вычислять минимальный суффикс любой подстроки за . Эта структура данных может быть построена за . |
Вышеописанная конструкция проста и работает для любого выбора канонических подстрок, но, к сожалению, она не может быть использована для достижения компромисса между временем запроса и временем построения. Далее мы предложим особый способ выбора канонических подстрок и опишем альтернативный метод построения. Этот способ основывается на предположении, что по данной строке длины мы можем найти минимальный суффикс для всех её префиксов за . Следовательно, нам удобно иметь много , которые являются префиксами друг друга. Тогда, естественным будет выбрать , поскольку все подстроки являются префиксами . К сожалению, подстроки, выбранные таким способом, не удовлетворяют условию , и, посему, нам необходимо немного изменить его.
Для мы определим . Для установим и определим таким образом:
Заметим, что если , то , в то время как, если , то . Очевидно, что количество таких подстрок, заканчивающихся в получается . Докажем далее, что канонические подстроки, выбранные вышеуказанным способом, имеют необходимые свойства.
| Теорема (3): |
Для любого и при мы имеем |
| Доказательство: |
|
Для неравенство, очевидно, выполняется. Рассмотрим . Обозначим через , как и ранее, . Если чётно, то нечётно и мы имеем , в то время как, для нечётного выполняется |
| Теорема (4): |
Для , величина может быть посчитана за константное время. |
| Доказательство: |
|
Положим . Заметим, что
. Таким образом, , и мы можем за константное время проверить, какое из этих трёх значений корректно. |
После построения улучшенного суфмассива, мы установили все биты в 1. После этого, для каждого мы посчитали минимальные суффиксы подстрок , как указано далее. Зафиксируем и разобьём на куски размером (где ) . Теперь каждый является префиксом конкатенации максимум 4х таких кусков. Вспомним, что по данной строке можно посчитать длины минимальных суффиксов всех её префиксов за линейное время с помощью одной из вариаций алгоритма Дюваля (Алгоритм 3.1 in [5]). Разделим на куски длиной (где ) и запустим этот алгоритм для каждых четырёх (или менее, в конце) последовательных кусков. Это даст нам минимальные суффиксы для всех , за время . Значение определено с помощью сравнения длины вычисленного минимального суффикса с . У нас фаз алгоритма, что даёт нам время и требуемой памяти.
Ссылки
- Wikipedia — Lyndon word
- MAXimal :: algo :: Декомпозиция Линдона. Алгоритм Дюваля
- Algebras, Rings, and Modules: Lie Algebras and Hopf Algebras", Michiel Hazewinkel, Nadezhda Mikhaĭlovna Gubareni, Vladimir V. Kirichenko, страница 242
- Computing minimal and maximal suffixes of a substring revisited
- On Minimal and Maximal Suffixes of a Substring