Редакция для Перестановочный шифр: решатель
Submitting an official solution before solving the problem yourself is a bannable offence.
1. Идея
Нужно восстановить исходное слово по зашифрованному.
Шифрование работало так:
- слово делили на блоки длины
m; - для каждого
jот1доmсимвол с позицииp[j]в исходном блоке переносили на позициюjв зашифрованном блоке.
Значит, если у нас уже есть зашифрованный блок, то можно сделать обратное действие:
- символ, который стоит в зашифрованном блоке на позиции
j, должен попасть в исходный блок на позициюp[j].
Именно это и нужно делать для каждого блока.
2. Наблюдения
Рассмотрим один блок длины m.
По условию при шифровании:
- в зашифрованный блок на позицию
jзаписали символ из исходного блока с позицииp[j].
Пусть зашифрованный блок хранится в строке s[start ... start + m - 1].
Тогда:
s[start + j]— это символ, который пришёл из позицииp[j]исходного блока.
Значит, при восстановлении надо сделать:
block[p[j] - 1] = s[start + j]
Здесь -1 нужен, потому что в условии позиции нумеруются с 1, а в массивах и строках программ — с 0.
3. Алгоритм
- Считать
m. - Считать перестановку
pдлиныm. - Считать зашифрованную строку
s. - Идти по строке
sблоками длиныm:- создать массив
blockдлиныm; - для каждого
jот0доm - 1записать:block[p[j] - 1] = s[start + j]
- добавить восстановленный блок к ответу.
- создать массив
- Вывести полученную строку.
4. Почему это работает
Докажем, что алгоритм правильно восстанавливает каждый блок.
По правилу шифрования для каждого j:
- символ из исходной позиции
p[j]попадает в зашифрованную позициюj.
Обозначим:
orig— исходный блок,enc— зашифрованный блок.
Тогда верно:
enc[j] = orig[p[j] - 1]
Нужно получить orig.
Если взять символ enc[j], то мы точно знаем, что в исходном блоке он стоял на позиции p[j] - 1. Поэтому при расшифровке надо выполнить:
orig[p[j] - 1] = enc[j]
Именно это и делает алгоритм.
Так как p — перестановка, все значения p[j] различны и покрывают все позиции от 1 до m. Значит:
- каждая позиция исходного блока будет заполнена ровно один раз;
- ни одна позиция не потеряется;
- блок восстановится однозначно.
Поскольку алгоритм независимо восстанавливает каждый блок, вся строка тоже будет восстановлена правильно.
5. Сложность
Пусть длина строки равна n.
- Каждый символ обрабатывается ровно один раз.
- Поэтому время работы равно
O(n). - Дополнительная память:
O(m)на один текущий блок,- и
O(n)на итоговый ответ.
6. Код на C++17
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main() {
int m;
cin >> m;
vector<int> p(m);
for (int i = 0; i < m; i++) {
cin >> p[i];
}
string s;
cin >> s;
string result;
result.reserve(s.size());
for (int start = 0; start < (int)s.size(); start += m) {
string block(m, ' ');
for (int j = 0; j < m; j++) {
block[p[j] - 1] = s[start + j];
}
result += block;
}
cout << result << '\n';
return 0;
}
7. Код на Python 3
m = int(input())
p = list(map(int, input().split()))
s = input()
parts = []
for start in range(0, len(s), m):
block = [''] * m
for j in range(m):
block[p[j] - 1] = s[start + j]
parts.append(''.join(block))
print(''.join(parts))
Комментарии