Лексикографія#9591

Лексикографія (Розбір)

У задачі слід розподілити задані n \cdot l літери по словам довжиною , відсортованим лексикографічно, так щоб -те слово було лексикографічно мінімально можливим.

Лексикографічний порядок слів визначається посимвольно зліва направо. Тому, щоб мінімізувати -те слово, потрібно як можна раніше (в перших позиціях) поставити до нього найменші можливі літери, не порушуючи сортування всіх слів.

Це приводить до наступного жадного алгоритму:

  1. Відсортуємо всі літери вхідного рядка за зростанням.

  2. Будемо будувати двовимірний масив символів

    v[1 .. n][1 .. l],

    де кожен рядок — окреме слово.

  3. Заповнюємо масив по стовпцях зліва направо, оскільки саме стовпці відповідають за позиції в словах і впливають на лексикографічний порядок.

Заповнення першого стовпця

  1. У перший стовпець роздаємо літери тільки рядкам з номерами від до , використовуючи найменших доступних літер.

  2. Ці літери розміщуються зверху вниз, щоб зберігався лексикографічний порядок.

  3. Рядки з номерами k + 1, ..., n на цьому етапі не заповнюються, оскільки вони не впливають на мінімальність -го слова.

Таким чином, ми гарантуємо, що перший символ -го слова мінімальний.

Заповнення наступних стовпців

Для кожного стовпця j = 2, 3, ..., l:

  1. Серед рядків 1, ..., k знаходимо мінімальний індекс , такий що рядки і збігаються на всіх попередніх стовпцях 1, ..., j - 1.

  2. Це множина рядків — всі слова, які на поточному префіксі не відрізняються від -го і тому зобов'язані залишатися не більшими за нього.

  3. У стовпці роздаємо найменші доступні літери тільки рядкам з номерами від до , зверху вниз.

  4. Повторюємо процес, поки всі символів -го слова не будуть зафіксовані.

Заповнення інших клітинок

Після того як -те слово повністю побудовано:

Коректність

Отже, отримане -те слово є лексикографічно найменшим.

Приклад

Розглянемо наступний тест

6 3 6
fgahhfgaaebdcbbebc

Тут потрібно скласти слів довжини , використовуючи всі літери, так щоб -те слово в лексикографічно відсортованому списку було мінімальним.

Відсортуємо рядок вхідних літер:

aaabbbbccdeeffgghh

Всього 18 = 6 \cdot 3 літер, що відповідає розміру таблиці 6 × 3.

Стовпець 1. Заповнюємо перший стовпець літерами зверху вниз від першої до шостої рядка, оскільки саме вона нас цікавить.

Стовпець 2. Тепер розглянемо рядки, чий префікс збігається з префіксом -ї рядка. Префікс -ї рядка після першого стовпця є "". Збігатимуться будуть рядки і . Мінімальний номер такого рядка pt = 4.

У другому стовпці роздаємо мінімальні доступні літери рядкам з номерами від до .

Стовпець 3. Знову шукаємо рядки, чий префікс збігається з "" (після заповнених двох стовпців). Збігатимуться будуть рядки і . Мінімальний номер такого рядка pt = 5. Роздаємо мінімальні доступні літери рядкам з номерами від до . Тепер -те слово повністю сформовано і дорівнює "".

Заповнення залишилися літер

Після фіксації -го слова залишилися літери можна розподілити довільним коректним чином. Наприклад, заповнимо таблицю по рядкам зверху вниз і зліва направо залишилися літерами у лексикографічному порядку.

Розглянемо перший тест

3 2 2
abcdef

Відсортуємо вхідний рядок літер:

Нам слід заповнити матрицю з слів по літери кожне, лексикографічно мінімізувавши при цьому -е слово.

Спочатку заповнюємо перший стовпець літерами зверху вниз, тільки для рядків з -ї по -ю, оскільки саме вони впливають на мінімальність другого слова.

Далі переходимо до другого стовпця. Його слід заповнювати, починаючи з найранішої рядка, префікс якої збігається з префіксом -ї рядка. У цьому випадку такою рядком є сама -я рядок, тому pt = 2. Заповнюємо другий стовпець літерами від рядка номер до -ї рядка.

Після цього залишилися літери розподіляємо у лексикографічному порядку по рядках зверху вниз і зліва направо, що завершує побудову коректної відповіді.

Реалізація алгоритму

Читаємо вхідні дані.

cin >> n >> l >> k;
cin >> s;

Відсортуємо літери вхідного рядку. Поточною оброблюваною буквою буде .

pos = 0;
sort(s.begin(), s.end());

Оголосимо матрицю результату , що складається з рядків і стовпців, де кожен рядок відповідає одному слову довжини .

vector<string> v(n, string(l, ' '));

Будемо розподіляти літери по стовпцю з номером , заповнюючи рядки з номерами від до k - 1 включно (нумерація рядків починається з нуля).

int pt = 0;
for (j = 0; j < l; j++)
{
  for (i = pt; i < k; i++)
    v[i][j] = s[pos++];

Рухаємо вказівник вперед (pt++) до тих пір, поки -ий символ (k - 1)-ої рядка не співпаде з -им символом рядку номер . В результаті вказує на рядок з найменшим номером, яка на поточному кроці збігається з префіксом рядку k - 1.

  while (v[pt][j] != v[k - 1][j]) pt++;
}

Після цього заповнюємо залишилися осередки матриці. Для цього послідовно проходимо по рядках зверху вниз і по стовпцях зліва направо, розподіляючи залишилися літери.

for (i = 0; i < n; i++)
for (j = 0; j < l; j++)
  if (v[i][j] == ' ') v[i][j] = s[pos++];

Виводимо відповідь.

for (i = 0; i < n; i++)
  cout << v[i] << endl;