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