例如,如果输入字符串是helloworld,我希望输出如下:
do
he
we
low
hell
hold
roll
well
word
hello
lower
world
...
一直到最长的单词,即helloworld子字符串的字谜。就像Scrabble一样。 输入字符串可以是任意长度,但很少超过16个字符。
我已经完成了搜索,并提出了像特里的结构,但我仍然不确定如何实际执行此操作。
答案 0 :(得分:14)
用于保存有效条目字典的结构将对效率产生巨大影响。将它组织为树,root是单个零字母“word”,空字符串。 root的每个子节点都是一个可能单词的单个首字母,其中的子节点是可能单词的第二个字母等,每个节点都标记为是否实际形成单词。
您的测试人员功能将是递归的。它以零字母开头,从有效条目的树中找到“”不是一个单词,但它确实有子项,所以你用你的起始单词(没有字母)递归地调用你的测试者,你的每一个可用的剩余字母都是输入字符串(在那一点上都是它们)。如果有效,请检查树中的每个单字母条目;如果孩子,重新调用测试器功能附加每个剩余的可用字母,等等。
例如,如果您的输入字符串是“helloworld”,那么您将首先使用“”调用递归测试器函数,并将剩余的可用字母“helloworld”作为第二个参数传递。函数看到“”不是单词,但是孩子“h”确实存在。所以它称自己为“h”和“elloworld”。功能看到“h”不是单词,但是孩子“e”存在。所以它称自己为“他”和“lloworld”。函数看到“e”被标记,所以“他”是一个单词,请注意。此外,孩子“l”存在,所以下一个呼叫是“hel”与“loworld”。它接下来会发现“地狱”,然后是“你好”,然后必须退出并可能接下来找到“空心”,然后再一次支持空字符串,然后接着以“e”字开头。
答案 1 :(得分:9)
我无法抗拒自己的实施。它通过按字母顺序对所有字母进行排序,并将它们映射到可以从中创建的单词来创建字典。这是一个O(n)启动操作,无需查找所有排列。您可以将字典实现为另一种语言的trie,以获得更快的加速。
“getAnagrams”命令也是一个O(n)操作,它搜索字典中的每个单词以查看它是否是搜索的子集。做getAnagrams(“无线电报”)“(一个20个字母的单词)在我的笔记本电脑上花了大约1秒钟,并返回了1496个字谜。
# Using the 38617 word dictionary at
# http://www.cs.umd.edu/class/fall2008/cmsc433/p5/Usr.Dict.Words.txt
# Usage: getAnagrams("helloworld")
def containsLetters(subword, word):
wordlen = len(word)
subwordlen = len(subword)
if subwordlen > wordlen:
return False
word = list(word)
for c in subword:
try:
index = word.index(c)
except ValueError:
return False
word.pop(index)
return True
def getAnagrams(word):
output = []
for key in mydict.iterkeys():
if containsLetters(key, word):
output.extend(mydict[key])
output.sort(key=len)
return output
f = open("dict.txt")
wordlist = f.readlines()
f.close()
mydict = {}
for word in wordlist:
word = word.rstrip()
temp = list(word)
temp.sort()
letters = ''.join(temp)
if letters in mydict:
mydict[letters].append(word)
else:
mydict[letters] = [word]
示例运行:
>>> getAnagrams("helloworld")
>>> ['do', 'he', 'we', 're', 'oh', 'or', 'row', 'hew', 'her', 'hoe', 'woo', 'red', 'dew', 'led', 'doe', 'ode', 'low', 'owl', 'rod', 'old', 'how', 'who', 'rho', 'ore', 'roe', 'owe', 'woe', 'hero', 'wood', 'door', 'odor', 'hold', 'well', 'owed', 'dell', 'dole', 'lewd', 'weld', 'doer', 'redo', 'rode', 'howl', 'hole', 'hell', 'drew', 'word', 'roll', 'wore', 'wool','herd', 'held', 'lore', 'role', 'lord', 'doll', 'hood', 'whore', 'rowed', 'wooed', 'whorl', 'world', 'older', 'dowel', 'horde', 'droll', 'drool', 'dwell', 'holed', 'lower', 'hello', 'wooer', 'rodeo', 'whole', 'hollow', 'howler', 'rolled', 'howled', 'holder', 'hollowed']
答案 2 :(得分:6)
您想要的数据结构称为Directed Acyclic Word Graph (dawg),Andrew Appel和Guy Jacobsen在他们的论文“世界上最快的拼字游戏计划”中对其进行了描述,遗憾的是他们选择不在网上免费提供。 ACM会员或大学图书馆将为您提供。
我已经用至少两种语言实现了这个数据结构 - 它简单,易于实现,而且速度非常快。
答案 3 :(得分:2)
您想要的是power set的实现。
另请参阅Eric Lipparts的博客,他在一段时间后发表了关于this very thing的博客
编辑:
这是我写的一个从给定字符串中获取powerset的实现...
private IEnumerable<string> GetPowerSet(string letters)
{
char[] letterArray = letters.ToCharArray();
for (int i = 0; i < Math.Pow(2.0, letterArray.Length); i++)
{
StringBuilder sb = new StringBuilder();
for (int j = 0; j < letterArray.Length; j++)
{
int pos = Convert.ToInt32(Math.Pow(2.0, j));
if ((pos & i) == pos)
{
sb.Append(letterArray[j]);
}
}
yield return new string(sb.ToString().ToCharArray().OrderBy(c => c).ToArray());
}
}
这个函数给了我构成传入字符串的字符的函数,然后我可以将它们用作字谜字典中的键......
Dictionary<string,IEnumerable<string>>
我创建了像这样的字谜字典...(可能有更有效的方式,但这很简单,而且足够快速,可以使用拼字游戏单词列表)
wordlist = (from s in fileText.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)
let k = new string(s.ToCharArray().OrderBy(c => c).ToArray())
group s by k).ToDictionary(o => o.Key, sl => sl.Select(a => a));
答案 4 :(得分:2)
一种简单的方法是生成所有“子串”,并为每个子串检查它是否是可接受单词集的一个元素。例如,在Python 2.6中:
import itertools
import urllib
def words():
f = urllib.urlopen(
'http://www.cs.umd.edu/class/fall2008/cmsc433/p5/Usr.Dict.Words.txt')
allwords = set(w[:-1] for w in f)
f.close()
return allwords
def substrings(s):
for i in range(2, len(s)+1):
for p in itertools.permutations(s, i):
yield ''.join(p)
def main():
w = words()
print '%d words' % len(w)
ss = set(substrings('weep'))
print '%d substrings' % len(ss)
good = ss & w
print '%d good ones' % len(good)
sgood = sorted(good, key=lambda w:(len(w), w))
for aword in sgood:
print aword
main()
会发出:
38617 words
31 substrings
5 good ones
we
ewe
pew
wee
weep
当然,正如其他回复指出的那样,有目的地组织数据可以大大加快运行时间 - 尽管快速anagram finder的最佳数据组织可能会有所不同......但这在很大程度上取决于性质你的词汇词典(几万,像这里 - 或数百万?)。应考虑散列图和“签名”(基于对每个单词中的字母进行排序),以及尝试&amp; c。
答案 5 :(得分:0)
与Tim J,Eric Lippert的博文一样,我想到的第一件事就是。我想补充一点,他写了一篇关于如何改善他第一次尝试表现的后续行动。
答案 6 :(得分:0)
我相信this question答案中的Ruby代码也可以解决您的问题。
答案 7 :(得分:0)
我最近在手机上玩了很多Wordfeud,如果我能拿出一些代码给我一个可能的单词列表,我很好奇。以下代码使用您的可用源代码字母(*表示通配符)和带有允许单词主列表(TWL,SOWPODS等)的数组,并生成匹配列表。它通过尝试从源字母构建主列表中的每个单词来实现此目的。
我在编写代码后发现了这个主题,它绝对不如John Pirie的方法或DAWG算法那么高效,但它仍然很快。
public IList<string> Matches(string sourceLetters, string [] wordList)
{
sourceLetters = sourceLetters.ToUpper();
IList<string> matches = new List<string>();
foreach (string word in wordList)
{
if (WordCanBeBuiltFromSourceLetters(word, sourceLetters))
matches.Add(word);
}
return matches;
}
public bool WordCanBeBuiltFromSourceLetters(string targetWord, string sourceLetters)
{
string builtWord = "";
foreach (char letter in targetWord)
{
int pos = sourceLetters.IndexOf(letter);
if (pos >= 0)
{
builtWord += letter;
sourceLetters = sourceLetters.Remove(pos, 1);
continue;
}
// check for wildcard
pos = sourceLetters.IndexOf("*");
if (pos >= 0)
{
builtWord += letter;
sourceLetters = sourceLetters.Remove(pos, 1);
}
}
return string.Equals(builtWord, targetWord);
}