Быстрый поиск наибольшей возрастающей подпоследовательности — различия между версиями
м (→Визуализация) |
м (→Пример) |
||
Строка 247: | Строка 247: | ||
|} | |} | ||
|} | |} | ||
− | <tex>merged</tex> аналогичен сортированному, т.к. предыдущих ключей нет. | + | <tex>\mathtt{merged}</tex> аналогичен сортированному, т.к. предыдущих ключей нет. |
{| class="wikitable" style="center" | {| class="wikitable" style="center" | ||
! colspan="5"|Ключи сортированного блока | ! colspan="5"|Ключи сортированного блока | ||
Строка 258: | Строка 258: | ||
| 4||1||5||2||3 | | 4||1||5||2||3 | ||
|} | |} | ||
− | Пропускаем их через <tex>LIS</tex>: | + | Пропускаем их через <tex>\mathrm{LIS}</tex>: |
{| class="wikitable" style="center" | {| class="wikitable" style="center" | ||
|-align="center" | |-align="center" | ||
Строка 275: | Строка 275: | ||
<tex>B: \{1, 2, 3\}</tex> | <tex>B: \{1, 2, 3\}</tex> | ||
− | <tex>merged: \{3, 4, 8, 9, 10\}</tex> | + | <tex>\mathtt{merged}: \{3, 4, 8, 9, 10\}</tex> |
===== Второй блок ===== | ===== Второй блок ===== | ||
− | Восстанавливаем элементы <tex>B: \{1, 2, 3\}</tex> из <tex>merged: \{3, 4, 8, 9, 10\}</tex>: <tex>\{3, 4, 8\}</tex>. | + | Восстанавливаем элементы <tex>B: \{1, 2, 3\}</tex> из <tex>\mathtt{merged}: \{3, 4, 8, 9, 10\}</tex>: <tex>\{3, 4, 8\}</tex>. |
{| | {| | ||
| || | | || | ||
Строка 301: | Строка 301: | ||
{| class="wikitable" style="center" | {| class="wikitable" style="center" | ||
|-align="center" | |-align="center" | ||
− | | colspan="8"|<tex>merged</tex> | + | | colspan="8"|<tex>\mathtt{merged}</tex> |
|-align="center" | |-align="center" | ||
| 1||2||3||4||5||6||8||12 | | 1||2||3||4||5||6||8||12 | ||
Строка 308: | Строка 308: | ||
{| class="wikitable" style="center" | {| class="wikitable" style="center" | ||
|-align="center" | |-align="center" | ||
− | | colspan="3"|<tex>ind's\#0</tex> — индексы текущих | + | | colspan="3"|<tex>\mathtt{ind's\#0}</tex> — индексы текущих |
|-align="center" | |-align="center" | ||
| 3||4||7 | | 3||4||7 | ||
Строка 315: | Строка 315: | ||
{| class="wikitable" style="center" | {| class="wikitable" style="center" | ||
|-align="center" | |-align="center" | ||
− | | colspan="5"|<tex>ind's\#1</tex> — индексы новых | + | | colspan="5"|<tex>\mathtt{ind's\#1}</tex> — индексы новых |
|-align="center" | |-align="center" | ||
| 1||2||5||6||8 | | 1||2||5||6||8 | ||
Строка 331: | Строка 331: | ||
| 1||2||5||4||3 | | 1||2||5||4||3 | ||
|} | |} | ||
− | Восстанавливаем порядок новых из <tex>ind's\#1</tex> и <tex>\xi</tex>: | + | Восстанавливаем порядок новых из <tex>\mathtt{ind's\#1}</tex> и <tex>\xi</tex>: |
{| class="wikitable" style="center" | {| class="wikitable" style="center" | ||
! colspan="5"| новые ключи | ! colspan="5"| новые ключи | ||
Строка 346: | Строка 346: | ||
| 3 || 4 || style="background:#FFCC00"| 7 || style="background: #77A9F4"| 7 | | 3 || 4 || style="background:#FFCC00"| 7 || style="background: #77A9F4"| 7 | ||
|} | |} | ||
− | <tex>LIS</tex> новых: | + | <tex>\mathrm{LIS}</tex> новых: |
{| class="wikitable" style="center" | {| class="wikitable" style="center" | ||
|-align="center" | |-align="center" | ||
Строка 363: | Строка 363: | ||
<tex>B: \{1, 2, 5, 8\}</tex> | <tex>B: \{1, 2, 5, 8\}</tex> | ||
− | <tex>merged: \{1,2,3,4,5,6,8,12\}</tex> | + | <tex>\mathtt{merged}: \{1,2,3,4,5,6,8,12\}</tex> |
===== Третий блок ===== | ===== Третий блок ===== | ||
− | Восстанавливаем элементы <tex>B: \{1, 2, 5, 8\}</tex> из <tex>merged: \{1,2,3,4,5,6,8,12\}</tex>: <tex>\{1, 2, 5, 12\}</tex>. | + | Восстанавливаем элементы <tex>B: \{1, 2, 5, 8\}</tex> из <tex>\mathtt{merged}: \{1,2,3,4,5,6,8,12\}</tex>: <tex>\{1, 2, 5, 12\}</tex>. |
{| | {| | ||
| || | | || | ||
Строка 390: | Строка 390: | ||
{| class="wikitable" style="center" | {| class="wikitable" style="center" | ||
|-align="center" | |-align="center" | ||
− | | colspan="8"|<tex>merged</tex> | + | | colspan="8"|<tex>\mathtt{merged}</tex> |
|-align="center" | |-align="center" | ||
| 1||2||5||7||11||12 | | 1||2||5||7||11||12 | ||
Строка 397: | Строка 397: | ||
{| class="wikitable" style="center" | {| class="wikitable" style="center" | ||
|-align="center" | |-align="center" | ||
− | | colspan="4"|<tex>ind's\#0</tex> — индексы текущих | + | | colspan="4"|<tex>\mathtt{ind's\#0}</tex> — индексы текущих |
|-align="center" | |-align="center" | ||
| 1||2||3||6 | | 1||2||3||6 | ||
Строка 404: | Строка 404: | ||
{| class="wikitable" style="center" | {| class="wikitable" style="center" | ||
|-align="center" | |-align="center" | ||
− | | colspan="2"|<tex>ind's\#1</tex> — индексы новых | + | | colspan="2"|<tex>\mathtt{ind's\#1}</tex> — индексы новых |
|-align="center" | |-align="center" | ||
| 4||5 | | 4||5 | ||
Строка 419: | Строка 419: | ||
| 1||2 | | 1||2 | ||
|} | |} | ||
− | Восстанавливаем порядок новых из <tex>ind's\#1</tex> и <tex>\xi</tex>: | + | Восстанавливаем порядок новых из <tex>\mathtt{ind's\#1}</tex> и <tex>\xi</tex>: |
{| class="wikitable" style="center" | {| class="wikitable" style="center" | ||
! colspan="2"| новые ключи | ! colspan="2"| новые ключи | ||
Строка 447: | Строка 447: | ||
<tex>B: \{1, 2, 3, 4, 5\}</tex> | <tex>B: \{1, 2, 3, 4, 5\}</tex> | ||
− | <tex>merged: \{1,2,5,7,11,12\}</tex> | + | <tex>\mathtt{merged}: \{1,2,5,7,11,12\}</tex> |
===== Восстановление НВП ===== | ===== Восстановление НВП ===== | ||
{| class="wikitable" style="center" | {| class="wikitable" style="center" | ||
− | ! colspan="12"| <tex>predecessor</tex> | + | ! colspan="12"| <tex>\mathtt{predecessor}</tex> |
|-align="center" | |-align="center" | ||
| 1||2||3||4||5||6||7||8||9||10||11||12 | | 1||2||3||4||5||6||7||8||9||10||11||12 | ||
Строка 457: | Строка 457: | ||
| ||1|| ||3||2||2||5||4|| ||3||7||8 | | ||1|| ||3||2||2||5||4|| ||3||7||8 | ||
|} | |} | ||
− | Начинаем восстановление с <tex>merged[5] = 11</tex>: | + | Начинаем восстановление с <tex>\mathtt{merged}[5] = 11</tex>: |
{| class="wikitable" style="center" | {| class="wikitable" style="center" | ||
|+ обратный порядок | |+ обратный порядок |
Версия 23:51, 7 января 2017
Задача: |
Дана перестановка . Требуется найти НВП за , где — длина НВП. |
Содержание
Алгоритм O(n log log n)
Нахождение длины НВП
Основная идея
Пусть
— входная перестановка.Для каждой длины элемент, что может быть последним в возрастающей подпоследовательности длины , запишем их в массив .
предполагаемой НВП находим наименьшийЕсли обрабатываемый элемент
больше последнего элемента какой-нибудь возрастающей последовательности, он может ее увеличить.Будем последовательно обрабатывать элементы
:- Если , значит с ним можно сделать максимальную, из уже рассмотренных, возрастающую подпоследовательность. Записываем его в конец . больше
- Иначе заменяет наименьший лучший элемент, из тех, что больше .
Следует отметить, что полученный массив также образует возрастающую последовательность, где мы должны выполнять операции приоритетную очередь, реализованную через Дерево ван Эмде Боаса. Таким образом получаем амортизированного времени на одну операцию.
, соответственно целесообразно использоватьПример
Типы операций
- Добавление элемента, который больше всех предыдущих:
- Замещение элемента более подходящим, т.е. добавление немаксимального элемента:
Последовательность
9 | 3 | 10 | 4 | 8 | 1 | 2 | 12 | 6 | 5 | 7 | 11 |
Состояние очереди при каждом добавлении
9 | 9 | ||||
3 | 3 | ||||
3 | 10 | 10 | |||
3 | 4 | 4 | |||
3 | 4 | 8 | 8 | ||
1 | 4 | 8 | 1 | ||
1 | 2 | 8 | 2 | ||
1 | 2 | 8 | 12 | 12 | |
1 | 2 | 6 | 12 | 6 | |
1 | 2 | 5 | 12 | 5 | |
1 | 2 | 5 | 7 | 7 | |
1 | 2 | 5 | 7 | 11 | 11 |
Псевдокод
int LIS(следующий B.delete(B.next(x)) else // добавленный элемент — максимальный // предыдущие значения не трогаем, очередь увеличилась k = k + 1 return k[n]) PriorityQueue B // рабочая приоритетная очередь int k = 0 // длина НВП for i = 1 to n x = [i] // в любом случае добавляем в очередь очередной элемент // устаревшие будем удалять B.insert(x) if B.next(x) // добавленный элемент — не максимальный // удаляем предыдущее значение — заменяем
Расширение алгоритма до нахождения НВП
Основная идея
Будем запоминать пары: для каждого элемента записываем его "предшественника".
Тогда, выбрав какой-нибудь лучший элемент для максимальной длины, мы можем легко восстановить НВП .
Общий вид алгоритма
9 | 9 | ||||
3 | 3 | ||||
3 | 10 | 10 | |||
3 | 4 | 4 | |||
3 | 4 | 8 | 8 | ||
1 | 4 | 8 | 1 | ||
1 | 2 | 8 | 2 | ||
1 | 2 | 8 | 12 | 12 | |
1 | 2 | 6 | 12 | 6 | |
1 | 2 | 5 | 12 | 5 | |
1 | 2 | 5 | 7 | 7 | |
1 | 2 | 5 | 7 | 11 | 11 |
predecessor | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
1 | 3 | 2 | 2 | 5 | 4 | 3 | 7 | 8 |
Псевдокод
vector<int> LIS([n]) PriorityQueue B int k = 0 int predecessor[n] // резервируем позиций for i = 1 to n x = [i] B.insert(x) predecessor[x] = B.prev(x) if B.next(x) B.delete(B.next(x)) else k = k + 1 // по цепочке от последнего элемента // восстанавливаем НВП int result[k] int cur = B.max() for i = k - 1 downto 0 result[i] = cur cur = predecessor[cur] return result
Оптимизация до O(n log log k)
Основная идея
Чтобы Дерево ван Эмде Боаса выполняло операции за , необходимо алфавит обрабатываемых значений уменьшить до .
Предположим, мы знаем такое приближение числа числом . Если мы разобьем всю последовательность на блоки из и нам удастся обрабатывать каждый как перестановку из элементов элементов, то мы получим асимптотическое время , а т.к. , то . (Мы будем обрабатывать блоки последовательно, т.е. с предыдущего блока у нас может остаться значений в очереди, которые дополняются значениями очередного блока — получаем верхнее ограничение в обрабатываемых возможных значений.)
Описанный здесь алгоритм подбора
и получение асимптотической оценки в других подразделах рассмотрено не будет, т.к. в основном это доказательство, сложного для понимания/реализации ничего нетРассмотрим последовательность
, где , — некоторое значение, меньшее .Будем последовательно для элементов этой последовательности запускать алгоритм. Если условие перестает выполняться, прерываем выполнение. Таким образом, время работы для каждого будет . Найдется такой , который окажется больше , и алгоритм успешно завершится.
Общее время работы — . Заметим, что , т.к. в противном случае , что противоречит тому, что — первый из тех, что больше . Следовательно, .
Получаем время работы
.Деление на блоки
Основная идея
Разделим исходную перестановку
на блоки .Получим сортированные варианты этих блоков цифровая сортировка дает нам время работы . Дополним каждый элемент номером блока, в котором он находится и смещением в этом блоке. Теперь, рассматривая номер блока как старший разряд, элемент как младший разряд, можно сортировать цифровой сортировкой за линейное время .
. ЛобоваяПерестановка смещений, образованная в сортированном блоке есть не что иное, как обратная перестановка перестановки, элементы которой соотносятся между собой как элементы исходного блока. Находим обратную перестановку к найденной, назовем ее
.Пример
Пусть
. Исходно:Блок | 1 | 1 | 1 | 1 | 1 | 2 | 2 | 2 | 2 | 2 | 3 | 3 |
9 | 3 | 10 | 4 | 8 | 1 | 2 | 12 | 6 | 5 | 7 | 11 | |
Смещение | 1 | 2 | 3 | 4 | 5 | 1 | 2 | 3 | 4 | 5 | 1 | 2 |
После сортировки:
Блок | 1 | 1 | 1 | 1 | 1 | 2 | 2 | 2 | 2 | 2 | 3 | 3 |
3 | 4 | 8 | 9 | 10 | 1 | 2 | 5 | 6 | 12 | 7 | 11 | |
Смещение | 2 | 4 | 5 | 1 | 3 | 1 | 2 | 5 | 4 | 3 | 1 | 2 |
Обратные перестановки (
):1 | 2 | 3 | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
4 | 1 | 5 | 2 | 3 | 1 | 2 | 5 | 4 | 3 | 1 | 2 |
Обработка блока
Основная идея
- Достаем из очереди ключи и конвертируем их в элементы .
- Классический merge только что полученных элементов с элементами нового блока, но с модификацией: генерируются 2 дополнительных массива - индексы элементов исходных массивов в новом. Слияние массивов назовем .
- На массив индексов, относящиеся к новому блоку, действует перестановка смещений сортированного варианта этого блока. Таким образом мы добиваемся эквиваленции отношений ключей к отношениям элементов в очереди и элементов в блоке, а ключи находятся в диапазоне .
- Первый массив индексов, который соответствует элементам, ранее находящимся в очереди, вновь кладутся в очередь (обычными -ами). Второй массив обрабатывается описанным выше алгоритмом , при том массив строится из ключей с помощью .
Визуализация
Помеченные зеленым — условные данные. Остальное — условные операции. Например
значит взять элементы из массива c индексами из . — здесь обозначает результат операции .Для первого блока содержательной является лишь ветка, начинающаяся с
, что не противоречит представленной схеме.Пример
Первый блок
|
|
аналогичен сортированному, т.к. предыдущих ключей нет.
Ключи сортированного блока | ||||
---|---|---|---|---|
2 | 4 | 5 | 1 | 3 |
4 | 1 | 5 | 2 | 3 |
Пропускаем их через
:4 | 4 | ||
1 | 1 | ||
1 | 5 | 5 | |
1 | 2 | 2 | |
1 | 2 | 3 | 3 |
Результат работы
Второй блок
Восстанавливаем элементы
из : .
|
|
|
|
|
Ключи сортированного блока | ||||
---|---|---|---|---|
1 | 2 | 5 | 4 | 3 |
1 | 2 | 5 | 4 | 3 |
Восстанавливаем порядок новых из
и :новые ключи | ||||
---|---|---|---|---|
1 | 2 | 8 | 6 | 5 |
Обновление старых ключей:
3 | 3 | ||
3 | 4 | 4 | |
3 | 4 | 7 | 7 |
новых:
1 | 4 | 7 | 1 | |
1 | 2 | 7 | 2 | |
1 | 2 | 7 | 8 | 8 |
1 | 2 | 6 | 8 | 6 |
1 | 2 | 5 | 8 | 5 |
Результат работы
Третий блок
Восстанавливаем элементы
из : .
|
|
|
|
|
Ключи сортированного блока | ||||
---|---|---|---|---|
1 | 2 |
1 | 2 |
Восстанавливаем порядок новых из
и :новые ключи | |
---|---|
4 | 5 |
Обновление старых ключей:
1 | 4 | 7 | 1 | |
1 | 2 | 7 | 2 | |
1 | 2 | 3 | 3 | |
1 | 2 | 3 | 6 | 6 |
новых:
1 | 2 | 3 | 4 | 4 | |
1 | 2 | 3 | 4 | 5 | 5 |
Результат работы
Восстановление НВП
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
1 | 3 | 2 | 2 | 5 | 4 | 3 | 7 | 8 |
Начинаем восстановление с
:11 | 7 | 5 | 2 | 1 |
1 | 2 | 5 | 7 | 11 |
См. также
- Дерево ван Эмде Боаса
- Задача о наибольшей возрастающей подпоследовательности
- Задача о наибольшей общей подпоследовательности
- Наибольшая общая возрастающая подпоследовательность
- Задача о наибольшей общей палиндромной подпоследовательности