我被问到一个问题
您将获得一个字符列表,与每个字符相关的分数和有效单词的字典(比如普通英语字典)。你必须从字符列表中形成一个单词,这样得分才是最大的,而且这个单词是有效的。
我可以想到一个解决方案,包括用字典制作的trie和可用字符的回溯,但无法正确表达。有没有人知道正确的方法或想出一个?
答案 0 :(得分:3)
首先迭代你的字母并计算你在英文字母中有多少个字符。将其存储在一个静态的,例如大小为26的char
数组中,其中第一个单元格对应a
秒到b
,依此类推。将此原始数组命名为cnt
。现在迭代所有单词,并为每个单词形成一个大小为26的类似数组。对于此数组中的每个单元格,检查cnt
中是否至少出现了多少个单元格。如果是这种情况,你可以写下这个词,否则你就可以了。如果您可以编写单词,则可以计算其分数,并在辅助变量中最大化分数。
这种方法具有线性复杂性,这也是您可能具有的最佳渐近复杂度(在您给出的所有输入都是线性大小之后)。
答案 1 :(得分:2)
受程序员的回答启发(最初我认为这种方法是O(n!)所以我放弃了它)。它需要O(nr of words)设置,然后每个问题需要O(2 ^(查询中的字符))。对于拼字游戏来说,它是256,所以可能是那个人要求你预期的。
首先观察到查询或单词中的字符顺序无关紧要,因此您希望将列表处理成一组字符。一种方法是“排序”这个词,所以“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()))
答案 2 :(得分:1)
为词典的每个单词构建 sorted-anagram的查找序列。这是一次性费用。
按字母排序我的意思是:如果单词是eat
,则表示为aet
。单词为tea
,您将其表示为aet
,bubble
表示为bbbelu
等
由于这是拼字游戏,假设您有8个牌(假设您想使用棋盘中的牌),则需要最多检查2 ^ 8种可能性。
对于8组中任何图块的子集,您可以对图块进行排序,并在anagram trie中查找。
最多有2 ^ 8个这样的子集,这可以通过更聪明的子集生成来优化(在重复切片的情况下)。
如果这是一个更普遍的问题,其中2 ^ {tile的数量}可能远高于字典中anagram-words的总数,那么使用频率计数可能会更好,就像在Ivaylo的答案中一样,可以使用多维范围查询来优化查找。 (在这种情况下是26个尺寸!)
很抱歉,这可能对您没有帮助(我认为您正在尝试做一些练习并有约束),但我希望这将有助于未来没有这些限制的读者。
答案 3 :(得分:0)
这是python中的一种蛮力方法,使用包含58,109个单词的英语词典。这种方法实际上非常快,每次运行时间约为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)
我使用的是the dictionary,为方便起见,删除了一个带连字符的条目。
答案 4 :(得分:0)
我们可以假设字典是固定的,分数是固定的,只有可用的字母会改变(如拼字游戏)吗?否则,我认为没有比先前建议的那样查找词典中的每个单词更好。
因此,我们假设我们处于这种情况。选择订单<尊重信件的费用。例如Q> Z> J> X> K> ..> A> E> I ..> Ú
将字典D替换为字典D'由D的单词的字谜组成,由前一个顺序排序的字母(例如,单词buzz被映射到zzbu),并且还删除重复项和长度为>的单词。 8,如果你的游戏中最多有8个字母。
然后使用D'其中子节点按其字母的值排序(因此根的第一个子节点为Q,第二个Z,..,最后一个子节点为U)。在trie的每个节点上,还存储通过此节点的单词的最大值。
给定一组可用字符,您可以以深度优先的方式探索trie,从左到右,并在内存中保留当前找到的最佳值。仅探索节点值大于当前最佳值的分支。这样,你将在第一个分支之后只探索几个分支(例如,如果你的游戏中有一个Z,探索任何以一个点字母开头的分支,因为它被丢弃,因为它最多得分8x1小于Z的值。我打赌你每次只会探索很少的分支。
答案 5 :(得分:0)
如果字典条目的数量相对较少(高达数百万),您可以使用强力:对于每个字,创建一个32位掩码。预处理数据:如果使用字母a / b / c /.../ z,则设置一位。对于六个最常见的英文字符,如果字母被使用了两次,则etaoin会设置另一个字符。
为您拥有的字母创建类似的位图。然后在字典中扫描单词,其中单词所需的所有位都在可用字母的位图中设置。您已将问题简化为一次性所有需要的字符,如果需要两次,则将六个最常见的字符缩小两次。你仍然需要检查是否可以形成一个单词,以防你有一个像“bubble”这样的单词,第一个测试只告诉你你有字母b,u,l,e但不一定是3 b。
通过在执行检查之前按点值对单词列表进行排序,第一次点击是最好的。这还有另一个好处:你可以计算你拥有的积分,而不用费力来检查更多积分。例如,泡沫有12个点。如果你只有11个点,则根本不需要检查这个单词(有一个小表,其中第一个单词的索引具有任意给定的点数)。
改进字谜:在表格中,只存储具有相同点数的不同位掩码(因此我们将有气泡和蓝色条目,因为它们具有不同的点值,但不适用于团队和配合)。然后为每个位掩码存储所有可能的单词,可能多于一个,并检查它们。这应该减少要检查的位掩码的数量。