Редакция для Шифровальщик/расшифровальщик Виженера


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.

1. Идея

Нужно реализовать шифр Виженера в двух режимах:

  • enc — шифрование;
  • dec — расшифровка.

Ключ используется циклически. Но есть важная деталь: символ ключа берётся только тогда, когда текущий символ текста — заглавная латинская буква. Пробелы не изменяются и не сдвигают позицию в ключе.

Значит, задача сводится к последовательному проходу по строке текста и посимвольному преобразованию.


2. Наблюдения

1. Как перевести букву в число

Для заглавных латинских букв удобно считать:

  • A = 0
  • B = 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. Алгоритм

  1. Считать mode, key, text.
  2. Создать пустую строку ответа.
  3. Запомнить длину ключа m.
  4. Завести счётчик pos = 0 — сколько букв текста уже обработано с использованием ключа.
  5. Для каждого символа 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 в ответ.
  6. Вывести ответ.

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))

Комментарии

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