Algorithm 以最高分解决拼字游戏
有人问我一个问题 你会得到一个字符列表,一个与每个字符相关的分数和一个有效单词词典(比如普通英语词典)。您必须从字符列表中生成一个单词,以便分数最大且单词有效Algorithm 以最高分解决拼字游戏,algorithm,dynamic-programming,string-matching,backtracking,trie,Algorithm,Dynamic Programming,String Matching,Backtracking,Trie,有人问我一个问题 你会得到一个字符列表,一个与每个字符相关的分数和一个有效单词词典(比如普通英语词典)。您必须从字符列表中生成一个单词,以便分数最大且单词有效 我可以想出一个解决方案,使用字典制作trie并使用可用字符进行回溯,但无法正确制定。有人知道正确的方法或想出了正确的方法吗?首先重复你的字母,并计算你在英语字母表中每个字符有多少次。将其存储在一个静态数组中,例如大小为26的char数组,其中第一个单元格对应于a第二个单元格对应于b,依此类推。将此原始数组命名为cnt。现在迭代所有单词,并
我可以想出一个解决方案,使用字典制作trie并使用可用字符进行回溯,但无法正确制定。有人知道正确的方法或想出了正确的方法吗?首先重复你的字母,并计算你在英语字母表中每个字符有多少次。将其存储在一个静态数组中,例如大小为26的
char
数组,其中第一个单元格对应于a
第二个单元格对应于b
,依此类推。将此原始数组命名为cnt。现在迭代所有单词,并为每个单词形成一个大小为26的类似数组。对于此数组中的每个单元格,请检查在cnt
中出现的次数是否至少相同。如果是这样的话,你可以写这个词,否则你就写不出来了。如果你能写出这个单词,你就可以计算它的分数,并在helper变量中最大化分数
这种方法将具有线性复杂度,这也是您可能具有的最佳渐进复杂度(在所有输入都是线性大小之后)。这里是python中的暴力方法,使用包含58109个单词的英语词典。这种方法实际上非常快,每次运行大约0.3秒
from random import shuffle
from string import ascii_lowercase
import time
def getValue(word):
return sum(map( lambda x: key[x], word))
if __name__ == '__main__':
v = range(26)
shuffle(v)
key = dict(zip(list(ascii_lowercase), v))
with open("/Users/james_gaddis/PycharmProjects/Unpack Sentance/hard/words.txt", 'r') as f:
wordDict = f.read().splitlines()
f.close()
valued = map(lambda x: (getValue(x), x), wordDict)
print max(valued)
这里是我使用的,为了方便起见,删除了一个连字符的条目。构建一个查找trie,只包含字典中每个单词的已排序的字谜。这是一次性费用 我所说的排序字谜是指:如果单词是
eat
,则表示为aet
。这个词是tea
,你把它表示为aet
,bubble
表示为bbbelu
等等
由于这是拼字游戏,假设您有8个磁贴(假设您想使用板上的一个磁贴),您将需要最多检查2^8个可能性
对于8组中的任何分片子集,可以对分片进行排序,并在anagram trie中进行查找
最多有2^8个这样的子集,通过更聪明的子集生成,这可能会得到优化(在重复瓷砖的情况下)
如果这是一个更一般的问题,其中2^{number of tiles}可能比字典中的字谜词总数高得多,那么最好像Ivaylo的答案那样使用频率计数,并且可以使用多维范围查询来优化查找。(在本例中为26个尺寸!)
很抱歉,这可能对您没有帮助(我想您正在尝试做一些练习并有一些限制),但我希望这将对没有这些限制的未来读者有所帮助。我们可以假设字典是固定的,分数是固定的,只有可用的字母会改变(如拼字游戏)?否则,我认为没有比按照前面的建议查找字典中的每个单词更好的了 让我们假设我们在这个环境中。选择一个尊重信件成本的订单。例如Q>Z>J>X>K>。>A>E>I..>美国 将你的字典D替换为一个字典D',该字典由D的单词的字谜和按上一个顺序排列的字母组成(例如,buzz这个单词被映射到zzbu),如果你的游戏中最多有8个字母,还可以删除重复的单词和长度大于8的单词 然后用单词D'构造一个trie,其中子节点按字母值排序(因此根的第一个子节点是Q,第二个子节点是Z,…,最后一个子节点是U)。在trie的每个节点上,还存储通过该节点的单词的最大值
给定一组可用字符,您可以以深度优先的方式探索trie,从左到右,并将找到的当前最佳值保存在内存中。仅浏览节点值大于当前最佳值的分支。这样,您将只探索第一个分支之后的几个分支(例如,如果您在游戏中有一个Z,则探索以一个点字母开头的任何分支都将被放弃,因为它最多将获得8x1的分数,该分数小于Z的值)。我打赌你每次只会探索很少几个分支。灵感来自程序员的答案(最初我认为这种方法是O(n!),所以我放弃了它)。它需要为每个问题设置O(字数),然后设置O(2^(查询中的字符))。这是指数级的,但在拼字游戏中一次只有7个字母块;所以你只需要检查128种可能性 第一个观察结果是,查询或word中字符的顺序无关紧要,因此您希望将列表处理为一组字符。一种方法是对单词进行“排序”,使“bac”、“cab”变成“abc” 现在,接受查询,并迭代所有可能的答案。保留/放弃每个字母的所有变体。以二进制形式更容易看到:1111保留全部,1110丢弃最后一个字母 然后检查字典中是否存在每种可能性(为简单起见,请使用哈希映射),然后返回得分最高的可能性
import nltk
from string import ascii_lowercase
from itertools import product
scores = {c:s for s, c in enumerate(ascii_lowercase)}
sanitize = lambda w: "".join(c for c in w.lower() if c in scores)
anagram = lambda w: "".join(sorted(w))
anagrams = {anagram(sanitize(w)):w for w in nltk.corpus.words.words()}
while True:
query = input("What do you have?")
if not query: break
# make it look like our preprocessed word list
query = anagram(sanitize(query))
results = {}
# all variants for our query
for mask in product((True, False), repeat=len(query)):
# get the variant given the mask
masked = "".join(c for i, c in enumerate(query) if mask[i])
# check if it's valid
if masked in anagrams:
# score it, also getting the word back would be nice
results[sum(scores[c] for c in masked)] = anagrams[masked]
print(*max(results.items()))
如果字典条目的数量相对较少(高达几百万),您可以使用蛮力:为每个单词创建一个32位掩码。预处理数据:如果使用字母a/b/c/../z,则设置一位。对于六个最常见的英文字符,如果字母使用两次,则设置另一位 为您拥有的字母创建一个类似的位图。然后,在位图中为可用字母设置单词所需的所有位时,扫描字典中的单词。您已将问题简化为一个单词,其中所有字符都需要一次,如果需要两次,则最常用的六个字符需要两次。你仍然需要检查一个词是否可以被使用