我如何解决"Crypt踢球"运动提议在"编程的挑战(编程竞赛的培训手册)"?
-
24-09-2019 - |
题
"编程的挑战(编程竞赛的培训手册)"可能是一个最好的练习簿上的算法。我已经解决的第11个练习,但是我现在坚持与"Crypt踢球"问题:
墓穴踢球
一个常见,但不安全的方法进行加密的文本是重排的字母表中的字母。换句话说,每一个字母表中的字母是一致的替代案文通过一些其他的信。为确保密是可逆的,没有两个字母替换成相同的信。你的任务是解密的几个编码的行文本,假设每个线使用一套不同的替代品,所有词语的解密的文本是从字典中已知的话。
输入
输入由一个包含整数n,随后通过n个小写的话说,每条线,按字母顺序排列。这些n话撰写的字典的话中可能出现的被解密的文本。
以下字典中的几行的输入。每个线是加密的,如上所述。有没有1 000多词在字典中。没有字超过16 字母。加密的线路只包含下情况的信和空间 没有超过80个字符长。
输出
解每个线和打印标准输出。如果有多个解决方案,任何一个人会做。
如果没有解决方案,替代的每一个字母通过一个星号。样本输入 6
和
迪克
jane
粉扑
点
yertlebjvg xsb hxsn xsb qymm xsb rqat xsb pnetfn
xxxx yyy zzzz www年aaa往ccc dddddd样本输出
迪克和简和粉扑点和yertle...
什么样的 战略 我应该采取为了解决这个运动?我想到一个经典的和野蛮的回溯的解决方案,但是我想避免这直到我找到的东西更加智能化。
PS:这不是家庭作业有关,我只是想提高我的总体技能。
解决方案
KeyArray将举行的替代表。
启动一个空KeyArray,这是0版本
比赛最长的加密词来最长的字典字,并添加到KeyArray (如果存在两个最长,挑选任何),这是版本1。
解密的一些信函的下一个长加密的话。
- 检查是否已解密的字母匹配的字母相同 位置中的任何词典词的长度相同。
- 如果没有匹配,回到版0,并尝试另一个词。
如果有些字母匹配,加入其余的字母KeyArray,这是第2版。
解密的一些信函的下一个长加密的话。
- 检查是否已解密的字母匹配的字母相同 位置中的任何词典字。
- 如果没有匹配,回到第1版,并尝试另一个词
- 如果有些字母匹配,加入其余的字母KeyArray,这是版本3。
重复,直到所有的话都是解密。
如果在版0没有时间最长的单词创造一个部分解中 较短的话,很可能没有解决方案。
其他提示
一个次要优化可以通过回溯运行之前列举的可能实现。在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'])}
如果你有一些单元素的可能性,则可以从输入消除它们并重新运行的算法。
修改:强>切换到集合而不是列表,并加入打印代码
我真的试图相当不同的方法。我内置从字典单词的线索。然后,我通过线索和句子一起递归(运行在DFS特里树)行走。
在每个空间都一定打在一个线索字的末尾,如果是我循环返回到根。一路上我跟踪号分配到目前为止,我所做的。如果说我有一个分配违背先前的任务,我会失败,并解开递归点我可以让下一个可能的分配新建分配FY。
这听起来很棘手,但它似乎工作得很好。它是真的并不难代码了!
另一个可能的优化,如果你有“足够”的文字处理,你知道该文本的语言,你可以使用字母频率(见:http://en.wikipedia.org/wiki/Letter_frequency
)。 6/7万字打交道时,但会以最快的方式,如果你有几页解码这当然是一个非常近似的方法。
编辑:关于最大的解决方案,你可以尝试提取词的一些特点,也如重复的字母。显然,陈述说粉扑在密文字典和qymm与双信结束只有四个字母的单词给出的字母3直接的答案。在更复杂的情况下,你应该能够缩小的可能性,每个字母的夫妇。
这里是一个Java实现更多改进的 算法 拟议的通过@卡洛斯*古铁雷斯.
改进是增加一个字图,以减少搜索空间的对话。例如,词语"abc"和"她"具有相同的模式,而"aac"和"她"不作为三种不同的字母的词会不匹配的一辆拖字母,不同的词。
此外,算法可以实现递归其是更为直观和合理的。