15.9. Генерация номера по объекту и объекта по номеру
Научимся сопоставлять комбинаторному объекту (например, перестановке) его номер в лексикографически упорядоченном списке всех объектов того же типа; или, наоборот, генерировать объект по его номеру.
Это бывает полезно, если хочется закодировать объект как можно меньшим количеством бит, а также если хочется использовать объект в качестве индекса массива (например, если при применении метода динамического программирования состояние описывается комбинаторным объектом).
Определение
Факторадическая система счисления: номер перестановки $p_1, \ldots, p_n$ в лексикографическом порядке — это сумма $\sum_{k=1}^{n} c_k \cdot (n-k)!$, где $c_k$ — количество элементов $p_{k+1}, \ldots, p_n$, меньших $p_k$. Всего перестановок длины $n$ ровно $n!$.
Номер по перестановке
Как вычислить номер перестановки $p_{1}, \ldots, p_{n}$ чисел $1, \ldots, n$ в списке всех перестановок длины $n$, упорядоченных лексикографически? Например, у нас есть упорядоченный лексикографически список перестановок длины 3:
$$ \begin{array}{|l|l|} \hline 0 & 1,2,3 \\ \hline 1 & 1,3,2 \\ \hline 2 & 2,1,3 \\ \hline 3 & 2,3,1 \\ \hline 4 & 3,1,2 \\ \hline 5 & 3,2,1 \\ \hline \end{array} $$Номер перестановки $p$ — это количество перестановок, лексикографически меньших $p$. Пусть $q$ — одна из таких перестановок. Тогда $q_1 = p_1, \ldots, q_{k-1} = p_{k-1}$, $x = q_k < p_k$ для некоторого $k$. При этом для фиксированных $k$, $x < p_k$ существует $(n-k)!$ различных перестановок $q$ (оставшиеся $n-k$ чисел могут идти в любом порядке).
Алгоритм: переберём $k$ — позицию самого левого несовпадения $q$ и $p$, переберём $x < p_k$ — значение $q_k$, просуммируем количество перестановок $q$ по всем таким $k, x$.
Правильные скобочные последовательности
Определение
Правильная скобочная последовательность (ПСП) из $2n$ символов — это последовательность скобок ( и ), в которой баланс (разность числа открывающих и закрывающих) любого префикса неотрицателен, а баланс всей последовательности равен нулю. Всего ПСП длины $2n$ ровно $C_n = \frac{1}{n+1}\binom{2n}{n}$ (числа Каталана).
Ниже представлен лексикографически упорядоченный список ПСП длины 8:
$$ \begin{array}{|c|c|c|c|} \hline 0 & (((()))) & 7 & (())(()) \\ \hline 1 & ((()())) & 8 & (())()() \\ \hline 2 & ((())()) & 9 & ()((())) \\ \hline 3 & ((()))() & 10 & ()(()()) \\ \hline 4 & (()(())) & 11 & ()(())() \\ \hline 5 & (()()()) & 12 & ()()(()) \\ \hline 6 & (()())() & 13 & ()()()() \\ \hline \end{array} $$Номер по ПСП
Будем искать номер ПСП $a$ длины $2n$ тем же методом: пусть $b$ лексикографически меньше $a$, причём $b_1 = a_1, \ldots, b_{k-1} = a_{k-1}$; ( $= b_k < a_k =$ ). Сколько существует таких ПСП $b$?
Поскольку баланс каждого префикса $b$ неотрицателен, баланс каждого суффикса $b$ неположителен. Тогда, если перевернуть суффикс $b_{k+1}, \ldots, b_{2n}$ и заменить скобки на противоположные, получится скобочная последовательность длины $2n - k$, баланс любого префикса которой неотрицателен, а баланс её целиком совпадает с балансом $b_1, \ldots, b_k$. Предподсчитаем количество таких последовательностей для каждой длины и каждого значения баланса динамическим программированием.
Перестановка по номеру
Вернёмся к перестановкам и решим обратную задачу: найти перестановку с заданным номером num.
Для любого $x$ от 1 до $n$ существует $(n-1)!$ перестановок, начинающихся с $x$. Тогда первый символ искомой перестановки — минимальное $x$ такое, что накопленное число не превышает num. Далее повторяем те же рассуждения для следующих позиций.
ПСП по номеру
Будем ставить на очередную позицию открывающую скобку, если номер ПСП меньше количества способов закончить ПСП после постановки этой скобки; иначе ставим закрывающую скобку и уменьшаем номер на количество «пропущенных» ПСП.
Свойства
Тот же метод (перебрать позицию и значение первого несовпадения, просуммировать количество способов «закончить» объект) работает с любым комбинаторным объектом, для которого можно эффективно подсчитать количество объектов с фиксированным префиксом.