Algorithm 给出一个单词列表,以一种高效的方式找到从该单词生成的所有可能单词

Algorithm 给出一个单词列表,以一种高效的方式找到从该单词生成的所有可能单词,algorithm,data-structures,word,Algorithm,Data Structures,Word,我的想法是,假设你有“回文”这个词。根据该单词,并根据我所学的可能单词词典(“我的词典”),您可以生成: do in drone mind plan line role lad pad drape ... 现在,假设我们以编程方式完成这项工作,给定一个计算机形式的词典(文本文件中的单词列表)。问题是如何在某种程度上有效地为每个单词找到可以从一个单词生成的单词。因此,对于字典中的每一个单词,我们都能找到从中产生的每一个单词 从给定单词生成单词的规则是,每个字母只能使用一次,但可以按任意顺序选择所

我的想法是,假设你有“回文”这个词。根据该单词,并根据我所学的可能单词词典(“我的词典”),您可以生成:

do
in
drone
mind
plan
line
role
lad
pad
drape
...
现在,假设我们以编程方式完成这项工作,给定一个计算机形式的词典(文本文件中的单词列表)。问题是如何在某种程度上有效地为每个单词找到可以从一个单词生成的单词。因此,对于字典中的每一个单词,我们都能找到从中产生的每一个单词

从给定单词生成单词的规则是,每个字母只能使用一次,但可以按任意顺序选择所需的字母

我开始尝试解决这个问题的简单方法是首先将所有单词加载到一个哈希表(JavaScript中的一个对象)中。似乎把它们放进trie会更好,但不确定。然后遍历哈希表中的每个单词。尝试为给定单词生成所有可能长度的字母的所有可能组合。我不知道如何从数学上计算出这是多少个组合,但似乎很多。所以对于“回文”来说是10个字母,但似乎是10个字母组合的大量组合,更不用说9、8、7、6

所以我想知道你该如何更有效地进行这项工作。我相信已经有人找到了很好地解决这个问题的诀窍


请注意,这不是一个家庭作业或面试问题,我只是想知道如何有效地做到这一点。

反过来做不是更简单吗?只需查看字典中的所有单词,看看该单词是否可以从目标单词生成。这样,我们所要做的就是一个接一个地减去字母,一旦发现一个字母丢失,我们就可以宣布失败并继续下一个单词。如果我们首先将目标单词转换成一个包(计数集),我们的检查将进行得非常快

这里有一个快速的例子。我需要几个实用函数:

func wordToCountedSet(_ s:String) -> NSCountedSet {
    return NSCountedSet(array: Array(s))
}
func canMake(_ s:String, from cs:NSCountedSet) -> Bool {
    let cs = cs.copy() as! NSCountedSet
    for letter in s {
        if !cs.contains(letter) {
            return false
        }
        cs.remove(letter)
    }
    return true
}
考虑一下NSCountedSet的效率(这是您的散列),以及
可以使
尽早且经常失败的事实

举例来说,假设我们的字典只包含“无人机”、“计划”、“行星”和“木琴”。我不会费心翻阅字典;我只想证明我们得到了正确的答案:

let cs = wordToCountedSet("palindrome")
canMake("drone", from:cs) // true
canMake("plan", from:cs) // true
canMake("planet", from:cs) // false
canMake("xylophone", from:cs) // false

有了“星球”,我们在最后一个“t”之前不会失败,但有了“木琴”,我们在第一个字母上就失败了。其他人必须减去测试单词的每个字母才能成功,但没有简单的方法;即便如此,成功所需的最长时间是字典中单词的字母数。这一切都很快,因为NSCountedSet是散列的。显然,我们可以添加一些快捷方式(例如,你不能使一个单词比原来的单词长),但这不是真正的重点

反过来做不是更简单吗?只需查看字典中的所有单词,看看该单词是否可以从目标单词生成。这样,我们所要做的就是一个接一个地减去字母,一旦发现一个字母丢失,我们就可以宣布失败并继续下一个单词。如果我们首先将目标单词转换成一个包(计数集),我们的检查将进行得非常快

这里有一个快速的例子。我需要几个实用函数:

func wordToCountedSet(_ s:String) -> NSCountedSet {
    return NSCountedSet(array: Array(s))
}
func canMake(_ s:String, from cs:NSCountedSet) -> Bool {
    let cs = cs.copy() as! NSCountedSet
    for letter in s {
        if !cs.contains(letter) {
            return false
        }
        cs.remove(letter)
    }
    return true
}
考虑一下NSCountedSet的效率(这是您的散列),以及
可以使
尽早且经常失败的事实

举例来说,假设我们的字典只包含“无人机”、“计划”、“行星”和“木琴”。我不会费心翻阅字典;我只想证明我们得到了正确的答案:

let cs = wordToCountedSet("palindrome")
canMake("drone", from:cs) // true
canMake("plan", from:cs) // true
canMake("planet", from:cs) // false
canMake("xylophone", from:cs) // false

有了“星球”,我们在最后一个“t”之前不会失败,但有了“木琴”,我们在第一个字母上就失败了。其他人必须减去测试单词的每个字母才能成功,但没有简单的方法;即便如此,成功所需的最长时间是字典中单词的字母数。这一切都很快,因为NSCountedSet是散列的。显然,我们可以添加一些快捷方式(例如,你不能使一个单词比原来的单词长),但这不是真正的重点

这可能是一个很好的数据结构应用程序。这样,您就可以开始查看单词的排列,如果您的词典中没有该单词,则可以提前退出。例如,有很多排列“<代码>回文> <代码> >开始<代码> dp>代码>,但是您不会考虑任何这些,因为<代码> dp>代码>是TIE中的死角。 当您查看
回文
时,您可能正在查看
模式
。您将把它添加到找到的单词列表中。对于
模式
,trie中还有更多的子级,但是对于
模式
模式
等,没有子级。您可以在搜索中切断这些整个分支。您将继续使用分支,该分支的子级将引导到诸如
model
modern
之类的词

使用python中的词典将单词列表转换为trie相当容易:

trie = {}

with open('words.txt') as words:
    for word in map(lambda w: w.strip(), words):
        cur = trie
        for l in word:
            cur  = cur.setdefault(l, {})
        cur['word'] = True # defined if this node indicates a complete word
有了一个合理的词表,根词典可能会为字母表中的每个字母都有一个键。但当你下降时,它会迅速变小。例如,对于查找
trie['w']
的小单词列表,将有
['a','e','h','i','o','r']
这样的键,表示字典中以
wa
we
,…开头的单词
trie['q']
可能只有一个键
u
,除非你有一本字典,里面有不太常见的单词

一旦你建立了trie,你就可以反复地去思考这个词的排列,在你找到它们的时候添加它们。这比查看所有排列要快得多,因为当没有更多的字母匹配键时,您会提前退出分支