Генерація колізій
Перегляд у форматі PDFНапишіть програму, яка згенерує багато (беззмістовних з точки зору природньої мови) string-ів однакової довжини, котрі мають однаковий hash, якщо рахувати його за правилами типу String мови Java, тобто ~s[0]*31^{n-1}+s[1]*31^{n-2}+...+s[n-2]*31+s[n-1]~ (де ~n~ – довжина рядка; інакше кажучи, обчислення hash-коду починається з числа 0, далі на кожному кроці спочатку старе значення домножається на 31, потім до нього додається ASCII-код чергової літери слова); обчислення hash-коду відбувається у знаковому 32-бітовому типі, з переповненнями.
Вхідні дані
В одному рядку через пропуски (пробіли) записані: спочатку потрібна однакова довжина рядків ~L~ (~8\leqslant L\leqslant 32~); потім кількість блоків ~K~ (~1\leqslant K\leqslant 2017~), таких, що всередині кожного блоку всі string-и повинні мати однаковий hash-код, а для різних блоків різні; потім кількість різних рядків всередині кожного з цих блоків ~N~ (~2\leqslant N\leqslant 2017~).
~K~ та ~N~ не будуть великими одночасно; гарантовано, що сумарний розмір всіх рядків результату не перевищуватиме одного мегабайту.
Результати
Слід вивести ~K\cdot N~ рядків, кожен з яких містить унікальний (у межах поточного запуску програми) string довжиною рівно ~L~, причому всі рядки з 1-го по ~N~-й повинні мати однаковий hash-код; якщо ~K\geqslant 2~, то всі рядки з ~(N+1)~-го по ~(2N)~-й повинні мати однаковий між собою, але відмінний від рядків з 1-го по ~N~-й, hash-код, і так далі. (В цьому поясненні вважаємо, що нумерація починається з одиниці). Таким чином, рядки з 1-го по ~N~-ий утворюватимуть один блок, рядки з ~(N+1)~-го по ~(2N)~-й (якщо ~K\geqslant 2~, тобто якщо їх взагалі треба виводити) утворюватимуть ще один блок, і так далі. Розділяти ці блоки якимись додатковими роздільниками не треба.
Символи рядків, які виводить Ваша програма, не зобов'язані бути буквами, але зобов'язані мати ASCII-коди від 33 до 126 (обидві межі включно).
Приклади
Вхід
8 3 3
Результат
BBBBBBBB
AaBBBBBB
BAaBBBBB
FFFFFFFF
EeFFFFFF
FEeFFFFF
SSSSSSSS
RrSSSSSS
SRrSSSSS
Примітки
У цій задачі можуть бути різні правильні відповіді. Ваша програма повинна виводити будь-яку одну правильну.
Поки що якість автоматичної перевірки цієї задачі вивірена не досить ретельно, в разі обґрунтованих сумнівів щодо правильності перевірки звертайтеся.
Головна ідея: якщо у двох сусідніх символах лівий збільшити на 1, а правий зменшити на 31 (у смислі відповідного збільшення/зменшення ASCII-кодів цих символів), то hash-код (обчислений саме за цією hash-функцією! це важливо!) лишиться незмінним. Однак, для проходження всіх тестів цієї задачі потрібно трошки посилити цю ідею:
- помітити, що такі збільшення та зменшення можна застосувати також і одночасно в різних парах сусідніх символів;
- помітити, що абсолютно те саме буде також і при збільшенні лівого на 2 та зменшенні правого на 62, або зменшенні лівого на 1 та збільшенні правого на 31, або зменшенні лівого на 2 та збільшенні правого на 62. (Забезпечити, щоб усі перелічені варіанти давали символи в проміжку ASCII-кодів від 33 до 126, неможливо; але це й не треба: досить забезпечити в тій самій парі сусідніх символів 2–3 зсуви (3–4 значення).)
Наприклад, усі «слова» з переліку
- 3&&
- 2E&
- 1d&
- 3%E
- 2DE
- 1cE
- 3$d
- 2Cd
- 1bd
мають однаковий Java-hash-код 50227. Їх вдалося зробити аж 9, при довжині всього 3 та дотримавшись обмеження «ASCII-коди всіх символів від 33 до 126».
Коментарі