Редакция для Морзе в двоичной записи


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. Идея

Нужно декодировать сообщение, записанное в необычном виде:

  • 0 — это точка азбуки Морзе;
  • 1 — это тире;
  • отдельные коды разделены пробелами;
  • символ / означает границу между словами.

Значит, задача сводится к двум шагам:

  1. построить таблицу соответствия: двоичный код -> буква или цифра;
  2. прочитать входную строку по токенам и для каждого токена:
    • если это /, добавить пробел;
    • иначе найти символ в таблице и добавить его в ответ.

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

Наблюдение 1. Удобно заранее создать словарь декодирования

В условии используются только латинские заглавные буквы AZ и цифры 09.
Для всех них известны стандартные коды Морзе.

Например:

  • A.-
  • B-...
  • 1.----

Но во входе вместо . и - используются 0 и 1.
Значит:

  • .- превращается в 01
  • -... превращается в 1000
  • .---- превращается в 01111

Если заранее перевести все обычные коды Морзе в двоичные и сохранить в словарь, декодирование станет очень простым.

Наблюдение 2. Вход удобно разбить на токены

Так как все элементы разделены пробелами, строку можно разбить на части:

  • двоичные коды символов;
  • символ /.

После этого остаётся просто пройти по списку токенов слева направо.

Наблюдение 3. Корректность входа гарантируется

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

3. Алгоритм

  1. Считать всю строку целиком.
  2. Задать список пар: обычный код Морзе и соответствующий символ.
  3. Построить словарь decode:
    • для каждого кода Морзе заменить:
      • . на 0
      • - на 1
    • сохранить результат как ключ, а букву или цифру как значение.
  4. Разбить входную строку по пробелам на токены.
  5. Создать пустой ответ.
  6. Для каждого токена:
    • если токен равен /, добавить в ответ пробел;
    • иначе добавить decode[token].
  7. Вывести получившуюся строку.

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

Докажем, что алгоритм действительно восстанавливает исходное сообщение.

Сначала строится таблица, в которой каждому допустимому двоичному коду сопоставлен ровно один символ.
Эта таблица верна, потому что она получена напрямую из стандартной азбуки Морзе заменой:

  • . на 0;
  • - на 1.

Затем вход разбивается на токены exactly по тому формату, который описан в условии:

  • коды символов разделены пробелами;
  • между словами стоит отдельный токен /.

Теперь рассмотрим любой токен:

  • если это /, по условию он означает разделение слов, значит в ответ нужно добавить пробел;
  • если это двоичный код, то по гарантии корректности он соответствует некоторой букве или цифре, и словарь возвращает именно этот символ.

Таким образом, каждый токен преобразуется правильно, а значит и вся строка декодируется правильно.

5. Сложность

Пусть n — длина входной строки.

  • Построение словаря занимает константное время, потому что символов всего 36.
  • Разбор строки и декодирование занимают O(n), так как каждый символ входа обрабатывается не более нескольких раз.

Итоговая сложность: O(n).

По памяти:

  • словарь имеет фиксированный размер;
  • дополнительно хранится ответ.

Итог: O(n) памяти с учётом ответа.

6. Код на C++17

#include <iostream>
#include <string>
#include <sstream>
#include <unordered_map>
#include <vector>

using namespace std;

int main() {
    string s;
    getline(cin, s);

    vector<pair<string, char>> morse = {
        {".-", 'A'}, {"-...", 'B'}, {"-.-.", 'C'}, {"-..", 'D'}, {"." , 'E'},
        {"..-.", 'F'}, {"--.", 'G'}, {"....", 'H'}, {"..", 'I'}, {".---", 'J'},
        {"-.-", 'K'}, {".-..", 'L'}, {"--", 'M'}, {"-.", 'N'}, {"---", 'O'},
        {".--.", 'P'}, {"--.-", 'Q'}, {".-.", 'R'}, {"...", 'S'}, {"-", 'T'},
        {"..-", 'U'}, {"...-", 'V'}, {".--", 'W'}, {"-..-", 'X'}, {"-.--", 'Y'},
        {"--..", 'Z'},
        {"-----", '0'}, {".----", '1'}, {"..---", '2'}, {"...--", '3'}, {"....-", '4'},
        {".....", '5'}, {"-....", '6'}, {"--...", '7'}, {"---..", '8'}, {"----.", '9'}
    };

    unordered_map<string, char> decode;
    for (auto &p : morse) {
        string bin;
        for (char c : p.first) {
            if (c == '.') {
                bin += '0';
            } else {
                bin += '1';
            }
        }
        decode[bin] = p.second;
    }

    stringstream ss(s);
    string token;
    string ans;

    while (ss >> token) {
        if (token == "/") {
            ans += ' ';
        } else {
            ans += decode[token];
        }
    }

    cout << ans << '\n';
    return 0;
}

7. Код на Python 3

s = input()

morse = [
    (".-", 'A'), ("-...", 'B'), ("-.-.", 'C'), ("-..", 'D'), (".", 'E'),
    ("..-.", 'F'), ("--.", 'G'), ("....", 'H'), ("..", 'I'), (".---", 'J'),
    ("-.-", 'K'), (".-..", 'L'), ("--", 'M'), ("-.", 'N'), ("---", 'O'),
    (".--.", 'P'), ("--.-", 'Q'), (".-.", 'R'), ("...", 'S'), ("-", 'T'),
    ("..-", 'U'), ("...-", 'V'), (".--", 'W'), ("-..-", 'X'), ("-.--", 'Y'),
    ("--..", 'Z'),
    ("-----", '0'), (".----", '1'), ("..---", '2'), ("...--", '3'), ("....-", '4'),
    (".....", '5'), ("-....", '6'), ("--...", '7'), ("---..", '8'), ("----.", '9')
]

decode = {}
for code, ch in morse:
    binary = ""
    for c in code:
        if c == '.':
            binary += '0'
        else:
            binary += '1'
    decode[binary] = ch

tokens = s.split()
ans = []

for token in tokens:
    if token == "/":
        ans.append(" ")
    else:
        ans.append(decode[token])

print("".join(ans))

Комментарии

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