Дан список слов, например с
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)
Теперь, если вы хотите найти все слова, которые отличаются на одну букву от слова "кошка", просто найдите слова "? 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
, этот подход не очень хорошо обобщает, но это очень сложная проблема в полной общности (это проблема поиска соседей в пространствах Хэмминга).