Редакция для Шифр Виженера: шифрование


Remember to use this editorial only when stuck, and not to copy-paste code from it. Please be respectful to the problem author and editorialist.
Submitting an official solution before solving the problem yourself is a bannable offence.

Идея

Нужно пройти по тексту слева направо и для каждой буквы зашифровать её с помощью очередной буквы ключа.

Шифр работает так:

  • буква A в ключе означает сдвиг на 0,
  • B — на 1,
  • ...
  • Z — на 25.

Если символ текста — заглавная латинская буква, мы сдвигаем её вперёд по алфавиту. Если символ не буква, например пробел, то:

  • он остаётся без изменений;
  • ключ на нём не продвигается.

Значит, достаточно хранить номер текущей буквы ключа и увеличивать его только тогда, когда обработали букву сообщения.

Наблюдения

  1. Ключ повторяется циклически. Если длина ключа равна m, то для очередной буквы текста используется символ ключа с индексом pos % m.

  2. Заглавные латинские буквы удобно переводить в числа от 0 до 25:

    • A -> 0
    • B -> 1
    • ...
    • Z -> 25
  3. Тогда шифрование буквы c со сдвигом shift выглядит так:

    • берём номер буквы c - 'A',
    • прибавляем shift,
    • берём остаток по модулю 26,
    • превращаем обратно в символ.
  4. Пробелы и другие небуквенные символы в ответ просто копируются. В этой задаче по условию в тексте есть заглавные буквы и пробелы, но решение легко работает и для любых других небуквенных символов.

Алгоритм

  1. Считать строку key.
  2. Считать строку text.
  3. Завести:
    • m — длину ключа,
    • pos = 0 — сколько букв текста уже было зашифровано.
  4. Пройти по всем символам строки text.
  5. Для каждого символа:
    • если это буква от A до Z:
      • вычислить сдвиг shift = key[pos % m] - 'A';
      • вычислить новую букву;
      • добавить её в ответ;
      • увеличить pos на 1;
    • иначе:
      • просто добавить символ в ответ.
  6. Вывести полученную строку.

Почему это работает

Докажем, что алгоритм строит ровно тот шифртекст, который требуется по условию.

Рассмотрим текст слева направо.

  • Если текущий символ не является заглавной латинской буквой, по условию он не должен изменяться, и ключ на нём не должен продвигаться. Алгоритм делает именно это: копирует символ как есть и не меняет 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))

Комментарии

Еще нет ни одного комментария.