假设我有“CAT”这个词。这些单词与“CAT”的区别在于一个字母(不是完整列表)
有一种优雅的方式来生成这个吗?显然,一种方法是通过蛮力。
pseduo代码:
while (0 to length of word)
while (A to Z)
replace one letter at a time, and check if the resulting word is a valid word
如果我有一个10个字母的单词,循环将运行26 * 10 = 260次。
有更好,更优雅的方法吗?
答案 0 :(得分:6)
给出一个单词列表,例如
words = set(line.strip().lower() for line in open('/usr/share/dict/words'))
你可以构建和编写“通配符”单词的索引,用通配符(比如“?”)替换单词的每个字符,这样例如“gat”和“fat”都被索引为“?at” “:
def wildcard(s, idx):
return s[:idx] + '?' + s[idx+1:]
def wildcarded(s):
for idx in xrange(len(s)):
yield wildcard(s, idx)
# list(wildcarded('cat')) returns ['?at', 'c?t', 'ca?']
from collections import defaultdict
index = defaultdict(list)
for word in words:
for w in wildcarded(word):
index[w].append(word)
现在,如果你想查找与“cat”相差一个字母的所有单词,只需查找“?at”,“c?t”和“ca?”并连接结果:
def near_words(word):
ret = []
for w in wildcarded(word):
ret += index[w]
return ret
print near_words('cat')
# outputs ['cat', 'bat', 'zat', 'jat', 'kat', 'rat', 'sat', 'pat', 'hat', 'oat', 'gat', 'vat', 'nat', 'fat', 'lat', 'wat', 'eat', 'yat', 'mat', 'tat', 'cat', 'cut', 'cot', 'cit', 'cay', 'car', 'cap', 'caw', 'cat', 'can', 'cam', 'cal', 'cad', 'cab', 'cag']
print near_words('stack')
# outputs ['stack', 'stack', 'smack', 'spack', 'slack', 'snack', 'shack', 'swack', 'stuck', 'stack', 'stick', 'stock', 'stank', 'stack', 'stark', 'stauk', 'stalk', 'stack']
如果最大字长为L
且字数为N
,则索引由O(NL)
指针组成,而查找算法及时运行O(L + number of results)
如果你想查找K
个字母而不是1
不同的所有单词,这种方法并不能很好地概括,但这是一个非常难以完全普遍的问题(它是在汉明空间寻找邻居的问题。)
答案 1 :(得分:3)
确定您的性能要求。
完全按照您上面的描述实施。
计时,看看你是否已满足这些要求。
仅在需要时进行优化(并且我愿意打赌它不是必需的,因为适合RAM的单词哈希表中的260个查找并不那么慢。)
< / LI> 醇>答案 2 :(得分:1)
你需要一个有效单词的字典来检查,否则问题就不会生成“单词”而是“字符串”。有很多可以免费在线获得,或者如果你在Linux上,大多数发行版都附带了/usr/share/dict/
中的字典文件。
有两种方法:
对于单词中的每个字母,将其替换为所有其他25个字符,并检查它是否在字典中。使用哈希表存储字典单词以进行有效查询。您只需要使用与搜索词长度相同的单词填充哈希表。这将是O(MN + 25N)= O(MN),其中M是字典中长度为N的字数,N是字长。
对于与搜索词长度相同的每个词典单词,请检查有多少个字符不同。这将是O(MN)。
虽然两者都属于同一复杂性类,但后者会丢弃与哈希表相关的O(25N)项和开销。
答案 3 :(得分:1)
对于:l =字长,w =字列表中的字数:
对于树词列表,您的算法是O(l。(l log w)),加上首先构造词列表的成本(即O(w log(w)) )(我假设这里有一棵树,如果你愿意,可以用哈希重做这个。)
这是O(l.w)
正如另一个答案已经暗示的那样,你不在乎这个词有a,b或z代替你想要改变的角色,你只关心它不是你开始的那个字母。因此,测试一个你不想要的组合,而不是所有组合。
所以:
for(each candidate word from the wordlist) {
difference = 0
for(each letter in your original word) {
does it match? if not, difference++
}
if difference = 1, store the candidate word as a solution
}
现在,你会争辩说你正在看78次比较而不是数千次,但这并不准确:为了利用一个词汇表来查看候选人是否可用,你的方法涉及创建一个内容 - 在您开始之前解决结构(树或散列),以及在运行后查找散列。上面的解决方案还允许您在每个测试单词中读取wordlist文件一次(无需将其保存在内存中以进行重新扫描)。你的解决方案可能会更快地同时在许多单词上执行此操作,但上述内容对于单个单词查找更有效,并且在每种情况下都可以提高内存效率。
相信其他答案的“计算差异”方法可以发现字差异......
答案 4 :(得分:1)
人类语言和字长的字典大小很小(~10 ** 5和~100),因此除非测量结果显示在你的情况下,否则会采用蛮力方法:
#!/usr/bin/env python
import string
ALL_WORDS = set(open('/usr/share/dict/words').read().lower().split())
ALPHABET = string.ascii_lowercase
def known(words): return set(w for w in words if w in ALL_WORDS)
def one_letter(word):
# http://norvig.com/spell-correct.html
splits = ((word[:i], word[i:]) for i in range(len(word) + 1))
replaces = (a + c + b[1:] for a, b in splits for c in ALPHABET if b)
return set(replaces)
from pprint import pprint
pprint(known(one_letter("cat")))
set(['bat',
'cab',
'cad',
'cal',
'cam',
'can',
'cap',
'car',
'cat',
'caw',
'cot',
'cut',
'eat',
'fat',
'hat',
'mat',
'nat',
'oat',
'pat',
'rat',
'sat',
'tat',
'vat'])
答案 5 :(得分:0)
无论如何,你需要遍历所有字母来检查它。但另一种方法是检查单词的字典,这对应于掩码?AT,C?T,CA? (哪里?可以是每个符号)
答案 6 :(得分:0)
如果字符串的长度总是匹配,一种方法是一次删除一个字母并比较两个字符串的结果,10个字符将是10个循环。
的问候, /吨
答案 7 :(得分:0)
迭代单词列表,并为每个单词计数不同的字母。如果计数大于1,请转到下一个单词。
更快的解决方案,如果字典是静态的并且有大量的单词要检查:创建一个字母矩阵。行是单词中的第一个字母,列是单词中的第二个字母。单元格是以给定的第一个和第二个字母开头的单词列表。当您想要查找给定单词的相似单词时,只迭代一行,然后只迭代一列。如果不在交叉单元格上,则每个迭代单词的所有其他字母必须匹配。在交叉单元格上,一个字母必须不同。
答案 8 :(得分:0)
如果确实希望优化运行时(我仍然说你可能不需要在任何合理的性能情况下),那么请翻阅一次字典,并运行你的算法每个字。
从损坏的单词创建一个映射到每个相应的正确拼写单词的列表。
我估计,处理至少30秒的20,000字,处理时间不会超过11分钟。
将生成的哈希表存储在磁盘上,并在需要时将其加载到内存中。然后通过简单地查找哈希表中的输入单词并找到正确拼写单词的相应列表来执行处理。
内存密集,但速度超快 - 如果你担心260个查找的性能,你必须处理成千上万的单词,这样的解决方案可能是你得到的最好的。