Как мне решить упражнение «Crypt Kicker», предложенное в «Задачах по программированию (Учебное пособие по программированию)»? - PullRequest
14 голосов
/ 01 февраля 2010

«Проблемы программирования (Учебное пособие по конкурсу по программированию)», вероятно, является одним из лучших упражнений на алгоритмы. Я выполнил первые 11 упражнений, но теперь я застрял с проблемой «Crypt Kicker»:

Crypt Kicker
Распространенным, но небезопасным методом шифрования текста является перестановка букв алфавита. Другими словами, каждая буква алфавита последовательно заменяется в тексте другой буквой. Чтобы гарантировать, что шифрование обратимо, никакие две буквы не заменяются одной и той же буквой.

Ваша задача - расшифровать несколько закодированных строк текста, предполагая, что каждая строка использует различный набор замен, и что все слова в расшифрованном тексте взяты из словаря известных слов.

Input
Входные данные состоят из строки, содержащей целое число n, за которым следуют n строчных слов, по одному на строку, в алфавитном порядке. Эти n слов составляют словарь слов, которые могут появиться в расшифрованном тексте.
За словарем следуют несколько строк ввода. Каждая строка зашифрована, как описано выше.

В словаре не более 1000 слов. Ни одно слово не превышает 16 буквы. Зашифрованные строки содержат только строчные буквы и пробелы и не превышать 80 символов в длину.

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

Пример ввода 6
и
член
джейн
слоеный
место
Yertle

bjvg xsb hxsn xsb qymm xsb rqat xsb pnetfn
xxxx гггг zzzz www гггг ааа bbbb ccc dddddd

Пример вывода
хуй и джейн и пух и пятно и yertle ...

Какую стратегию мне следует предпринять, чтобы выполнить это упражнение? Я думал о классическом и жестоком решении возврата, но я стараюсь избегать этого, пока не найду что-то более умное.

PS: Это не домашнее задание, я просто пытаюсь улучшить свои общие навыки.

Ответы [ 5 ]

9 голосов
/ 01 февраля 2010

KeyArray будет содержать таблицу замены.

  • Начните с пустого KeyArray, это версия 0

  • Сопоставьте самое длинное зашифрованное слово с самым длинным словарным словом и добавьте в KeyArray (если есть два самых длинных, выберите любой), это версия 1.

  • Расшифруйте некоторые буквы следующего самого длинного зашифрованного слова.

  • Проверьте, совпадают ли расшифрованные буквы с той же самой буквой позиция в любом словарном слове одинаковой длины.
  • Если ничего не найдено, вернитесь к версии 0 и попробуйте другое слово.
  • Если некоторые буквы совпадают, добавьте остальные буквы в KeyArray, это версия 2.

  • Расшифруйте некоторые буквы следующего самого длинного зашифрованного слова.

  • Проверьте, совпадают ли расшифрованные буквы с той же самой буквой Положение в любом словаре слова.
  • Если ничего не найдено, вернитесь к версии 1 и попробуйте другое слово
  • Если некоторые буквы совпадают, добавьте остальные буквы в KeyArray, это версия 3.

Повторяйте, пока все слова не расшифрованы.

Если в версии 0 ни одно из самых длинных слов не создает частичную расшифровку в короче говоря, очень вероятно, что нет решения.

3 голосов
/ 01 февраля 2010

Незначительная оптимизация может быть выполнена путем перечисления возможностей перед выполнением возврата. В Python:

dictionary = ['and', 'dick', 'jane', 'puff', 'spot', 'yertle']
line = ['bjvg', 'xsb', 'hxsn', 'xsb', 'qymm', 'xsb', 'rqat', 'xsb', 'pnetfn']

# ------------------------------------

import collections

words_of_length = collections.defaultdict(list)

for word in dictionary:
  words_of_length[len(word)].append(word)

possibilities = collections.defaultdict(set)
certainities = {}

for word in line:
    length = len(word)
    for i, letter in enumerate(word):
        if len(words_of_length[length]) == 1:
            match = words_of_length[length][0]
            certainities[letter] = match[i]
        else:
            for match in words_of_length[length]:
              possibilities[letter].add(match[i])

for letter in certainities.itervalues():
    for k in possibilities:
        possibilities[k].discard(letter)

for i, j in certainities.iteritems():
    possibilities[i] = set([j])

# ------------------------------------

import pprint
pprint.pprint(dict(possibilities))

Выход:

{'a': set(['c', 'f', 'o']),
 'b': set(['d']),
 'e': set(['r']),
 'f': set(['l']),
 'g': set(['f', 'k']),
 'h': set(['j', 'p', 's']),
 'j': set(['i', 'p', 'u']),
 'm': set(['c', 'f', 'k', 'o']),
 'n': set(['e']),
 'p': set(['y']),
 'q': set(['i', 'j', 'p', 's', 'u']),
 'r': set(['j', 'p', 's']),
 's': set(['n']),
 't': set(['t']),
 'v': set(['c', 'f', 'o']),
 'x': set(['a']),
 'y': set(['i', 'p', 'u'])}

Если у вас есть несколько одноэлементных возможностей, вы можете исключить их из входных данных и повторно запустить алгоритм.

РЕДАКТИРОВАТЬ: Переключено на установку вместо списка и добавлен код печати. ​​

2 голосов
/ 17 октября 2011

Я на самом деле попробовал довольно другой подход. Я построил три из словаря слов. Затем я рекурсивно прохожу через три и предложение вместе (проходя через три в DFS).

В каждом пробеле я проверяю, ударил ли я конец слова в дереве и, если это так, возвращаюсь к корню. По пути я отслеживаю назначения писем, которые я сделал до сих пор. Если когда-либо у меня есть задание, которое противоречит предыдущему заданию, я проваливаюсь и раскрываю рекурсию до такой степени, что могу сделать следующее возможное задание.

Звучит сложно, но, кажется, работает довольно хорошо. И это действительно не так сложно, чтобы закодировать!

0 голосов
/ 25 августа 2018

Вот реализация Java с большим количеством усовершенствований алгоритма , предложенного @Carlos Gutiérrez.

Алгоритм и решение Crypt Kicker, что пошло не так?

  • Уточнение заключается в добавлении словарного шаблона, чтобы уменьшить пространство для поиска слов. Например, слова «abc» и «her» имеют один и тот же шаблон, в то время как слова «aac» и «her» не совпадают, так как слово из трех разных букв не будет соответствовать букве из двух букв.

  • Более того, алгоритм может быть реализован рекурсивно, что является более интуитивным и разумным.

0 голосов
/ 01 февраля 2010

Другая возможная оптимизация: если у вас «достаточно» текста для работы и вы знаете язык текста, вы можете использовать буквенные частоты (см .: http://en.wikipedia.org/wiki/Letter_frequency). Это, конечно, очень приблизительный подход при работе с 6/7 словами, но он будет самым быстрым, если у вас есть несколько страниц для декодирования.

РЕДАКТИРОВАТЬ: о решении Макса, вы можете попытаться извлечь некоторые характеристики слова, такие как повторяющиеся буквы. Очевидно, что отметка о том, что слой в словаре и qymm в зашифрованном тексте являются единственными четырехбуквенными словами, заканчивающимися двойной буквой, дает прямой ответ для 3 букв. В более сложных сценариях вы должны быть в состоянии сузить возможности для каждой пары букв.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...