Редакция для Шифровальщик/расшифровальщик Виженера
Submitting an official solution before solving the problem yourself is a bannable offence.
1. Идея
Нужно реализовать шифр Виженера в двух режимах:
enc— шифрование;dec— расшифровка.
Ключ используется циклически. Но есть важная деталь: символ ключа берётся только тогда, когда текущий символ текста — заглавная латинская буква. Пробелы не изменяются и не сдвигают позицию в ключе.
Значит, задача сводится к последовательному проходу по строке текста и посимвольному преобразованию.
2. Наблюдения
1. Как перевести букву в число
Для заглавных латинских букв удобно считать:
A = 0B = 1- ...
Z = 25
Тогда:
- сдвиг по ключу равен
key[i] - 'A'; - буква текста тоже переводится в число от
0до25.
2. Как выполнять сдвиг
Если текущая буква текста имеет номер x, а буква ключа задаёт сдвиг shift, то:
- при
enc: получаем(x + shift) % 26 - при
dec: получаем(x - shift + 26) % 26
Добавление 26 нужно, чтобы не получить отрицательное число перед взятием остатка.
3. Ключ циклический
Если длина ключа равна m, а уже использовано pos букв ключа, то текущая буква ключа находится в позиции:
pos % m
4. Пробелы не расходуют ключ
По условию ключ применяется только к заглавным латинским буквам. Значит:
- если символ — буква от
AдоZ, шифруем или расшифровываем его и увеличиваемpos; - иначе просто копируем символ в ответ.
Хотя в этой задаче в тексте встречаются только заглавные буквы и пробелы, решение удобно писать в общем виде: всё, что не заглавная буква, оставляем как есть.
3. Алгоритм
- Считать
mode,key,text. - Создать пустую строку ответа.
- Запомнить длину ключа
m. - Завести счётчик
pos = 0— сколько букв текста уже обработано с использованием ключа. - Для каждого символа
cв тексте:- если
c— заглавная латинская буква:- взять текущий сдвиг
shift = key[pos % m] - 'A'; - перевести
cв числоx = c - 'A'; - если
mode == "enc", заменитьxна(x + shift) % 26; - иначе заменить
xна(x - shift + 26) % 26; - добавить в ответ букву
'A' + x; - увеличить
posна 1;
- взять текущий сдвиг
- иначе:
- просто добавить
cв ответ.
- просто добавить
- если
- Вывести ответ.
4. Почему это работает
Рассмотрим любой символ текста.
Случай 1. Символ — заглавная латинская буква
По условию именно для таких символов нужно применять шифр Виженера.
Алгоритм делает ровно это:
- выбирает нужную букву ключа по циклу через
pos % m; - переводит и букву текста, и букву ключа в числа от
0до25; - выполняет сдвиг вперёд в режиме
encили назад в режимеdec; - возвращает результат обратно в букву.
Таким образом, каждая буква обрабатывается строго по правилу задачи.
Случай 2. Символ не является заглавной латинской буквой
По условию такие символы должны остаться без изменений и не расходовать ключ.
Алгоритм просто копирует символ в ответ и не увеличивает pos. Значит, следующая буква текста действительно будет обрабатываться той же следующей буквой ключа, как и требуется.
Почему ключ используется правильно
Счётчик pos увеличивается только на заглавных буквах текста. Поэтому номер буквы ключа всегда совпадает с количеством уже обработанных букв, к которым ключ применялся. Это точно соответствует условию циклического использования ключа только на буквах.
Следовательно, алгоритм формирует ровно тот результат, который требуется в обоих режимах.
5. Сложность
Пусть длина текста равна n.
- Время работы:
O(n), потому что каждый символ обрабатывается один раз. - Память:
O(n)на хранение ответа.
6. Код на C++17
#include <iostream>
#include <string>
using namespace std;
int main() {
string mode, key, text;
getline(cin, mode);
getline(cin, key);
getline(cin, text);
string result;
int m = (int)key.size();
int pos = 0;
for (char c : text) {
if (c >= 'A' && c <= 'Z') {
int shift = key[pos % m] - 'A';
int x = c - 'A';
if (mode == "enc") {
x = (x + shift) % 26;
} else {
x = (x - shift + 26) % 26;
}
result += char('A' + x);
pos++;
} else {
result += c;
}
}
cout << result << '\n';
return 0;
}
7. Код на Python 3
mode = input()
key = input()
text = input()
result = []
m = len(key)
pos = 0
for c in text:
if 'A' <= c <= 'Z':
shift = ord(key[pos % m]) - ord('A')
x = ord(c) - ord('A')
if mode == 'enc':
x = (x + shift) % 26
else:
x = (x - shift) % 26
result.append(chr(ord('A') + x))
pos += 1
else:
result.append(c)
print(''.join(result))
Комментарии