Задачка на Python: расшифруйте строку

python

Ссылка на оригинал задачи: http://www.pythonchallenge.com/pc/def/map.html. Обратите на неё внимание, она нам ещё пригодится.

Условие

Есть три пары:

  • K → M;
  • O → Q;
  • E → G.

С их помощью нужно расшифровать следующий текст:

g fmnc wms bgblr rpylqjyrc gr zw fylb. rfyrq ufyr amknsrcpq ypc dmp. bmgle gr gl zw fylb gq glcddgagclr ylb rfyr’q ufw rfgq rcvr gq qm jmle. sqgle qrpgle.kyicrpylq() gq pcamkkclbcb. lmu ynnjw ml rfc spj.

Подсказки

Подсказка из ссылки: map. Также вы могли заметить, что между символами в парах находится ровно одна буква: K→L→M, O→P→Q, E→F→G. Кроме того, первой буквой в тексте является g, и если допустить, что оригинальное сообщение на английском, то можно предположить, что g — на самом деле i. Что у нас находится между g и i? Правильно, G→H→I. Поэтому давайте сдвинем каждый символ вправо на две позиции, y сдвинем к a и z сдвинем к b.

Прим.перев. В этой задаче применён шифр Цезаря.

Текст не очень длинный, поэтому просто скопируем его в REPL:

>>> raw = "g fmnc wms bgblr rpylqjyrc gr zw fylb. rfyrq ufyr amknsrcpq ypc dmp. bmgle gr gl zw fylb gq glcddgagclr ylb rfyr'q ufw rfgq rcvr gq qm jmle. sqgle qrpgle.kyicrpylq() gq pcamkkclbcb. lmu ynnjw ml rfc spj."

Решение 1

Пара функций, которые помогут в преобразовании символов:

  • ord(): сопоставляет символу целое число — его номер в таблице Unicode;
  • chr(): сопоставляет целому числу символ по таблице Unicode.

Например:

>>> chr(65)
'A'
>>> ord('A')
65

Сдвигаем символы:

>>> ord('k') 
107
>>> ord('k') + 2
109
>>> chr(ord('k') + 2)
'm'

Но с z так сделать не получится:

>>> chr(ord('z') + 2)
'|'

Чтобы всё заработало, посчитаем расстояние от a до z:

>>> (ord('z') + 2) - ord('a')
27

Если оно больше 26 — найдём остаток от деления на 26:

>>> ((ord('z') + 2) - ord('a')) % 26
1

Добавим его к a:

>>> chr(((ord('z') + 2) - ord('a')) % 26 + ord('a'))
'b'

И вынесем всё в отдельную функцию:

def decode_char(char):
    if 'a' <= char <= 'z':
        t = ord(char) + 2
        t = (t - ord('a')) % 26
        c = chr(t + ord('a'))
        return c
    else:
        return char

Расшифруем весь текст:

>>> result = ""
>>> for c in raw:
...    result += decode_char(c) 
...
... 
>>> result
"i hope you didnt translate it by hand. thats what computers are for. doing it in by hand is inefficient and that's why this text is so long. using string.maketrans() is recommended. now apply on the url."

Так мы обычно записываем циклы в Java или C, но в Python есть способ получше —  генераторы:

>>> ''.join(decode_char(c) for c in raw)
"i hope you didnt translate it by hand. thats what computers are for. doing it in by hand is inefficient and that's why this text is so long. using string.maketrans() is recommended. now apply on the url."

Раз уж нам предлагают использовать maketrans(), давайте именно так и сделаем.

Решение 2

В Python 3 maketrans() не является функцией модуля string; для вызова надо использовать str.maketrans() или bytes.maketrans():

>>> table = str.maketrans("abcdefghijklmnopqrstuvwxyz", "cdefghijklmnopqrstuvwxyzab")
>>> raw.translate(table)
"i hope you didnt translate it by hand. thats what computers are for. doing it in by hand is inefficient and that's why this text is so long. using string.maketrans() is recommended. now apply on the url."

Примечание В Python 2 maketrans() является функцией модуля string, поэтому сначала придётся её импортировать:

import string

table = string.maketrans("from", "to")

Решение 3

Теперь давайте напишем свой вариант maketrans. На вход подаются две строки, затем символы из первой соотносятся с символами из второй.

Определяем строки:

>>> a = "abcdefghijklmnopqrstuvwxyz,. '()"
>>> b = "cdefghijklmnopqrstuvwxyzab,. '()"

Применяем zip:

>>> list(zip(a, b))
[('a', 'c'), ('b', 'd'), ('c', 'e'), ('d', 'f'), ('e', 'g'), ('f', 'h'), ('g', 'i'), ('h', 'j'), ('i', 'k'), ('j', 'l'), ('k', 'm'), ('l', 'n'), ('m', 'o'), ('n', 'p'), ('o', 'q'), ('p', 'r'), ('q', 's'), ('r', 't'), ('s', 'u'), ('t', 'v'), ('u', 'w'), ('v', 'x'), ('w', 'y'), ('x', 'z'), ('y', 'a'), ('z', 'b'), (',', ','), ('.', '.'), (' ', ' '), ("'", "'"), ('(', '('), (')', ')')]

Создаём словарь:

>>> d = dict(zip(a, b))
>>> d
{'t': 'v', 'g': 'i', 'b': 'd', 'i': 'k', ',': ',', 'v': 'x', 'u': 'w', 'd': 'f', 'e': 'g', 'h': 'j', 'm': 'o', "'": "'", '(': '(', '.': '.', 'q': 's', 'l': 'n', 'a': 'c', 'x': 'z', ' ': ' ', 'f': 'h', 'o': 'q', 'w': 'y', 'n': 'p', 'c': 'e', 'p': 'r', 's': 'u', 'z': 'b', 'j': 'l', 'y': 'a', 'r': 't', 'k': 'm', ')': ')'}

Переводим всю строку:

>>> "".join(d[x] for x in raw)
"i hope you didnt translate it by hand. thats what computers are for. doing it in by hand is inefficient and that's why this text is so long. using string.maketrans() is recommended. now apply on the url."

Следующий уровень

Вспомним ссылку из начала статьи. Применим нашу функцию на слове map и получим ocr:

>>> "map".translate(str.maketrans("abcdefghijklmnopqrstuvwxyz", "cdefghijklmnopqrstuvwxyzab"))
'ocr'

Теперь у вас есть ссылка на следующую задачку: http://www.pythonchallenge.com/pc/def/ocr.html.

Перевод статьи «Python Challenge - Level 1»

Вакансии в тему:

Лого компании «Алгоритмика»
JavaScript Developer (React)
JavaScript Developer (React)
Алгоритмика, Москва, до 150 000 ₽
Лого компании «ОСӠ»
C++/C# разработчик
C++/C# разработчик
ОСӠ, Москва, от 80 000 до 120 000 ₽
Лого компании «12Go Asia»
PHP-разработчик
PHP-разработчик
12Go Asia, от 1 500 до 2 500 $ (до налогов)