Это может быть хорошим приложением структуры данных trie . Это позволяет вам начать просматривать перестановки слова, выходя рано, если нет шансов, что слово будет в вашем словаре. Например, существует множество перестановок palindrome
, которые начинаются с dp
, но вы не будете рассматривать ни одну из них, потому что dp
- это тупик в процессе.
Когда вы просматриваете palindrome
, вы можете смотреть на mode
. Вы добавите это в список найденных слов. В этом списке есть еще дети для mode
, но нет для modep
, modei
и т. Д. Вы можете отрезать все эти ветви в своем поиске. Вы просто продолжите ветку, у которой есть дети, ведущие к таким словам, как model
, modern
.
Довольно просто превратить список слов в три с помощью словаря в python:
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
, если у вас нет словаря с менее распространенными словами.
После того, как вы построили свое дерево, вы можете итеративно подумать о перестановках рассматриваемого слова, добавляя слова по мере их нахождения. Это будет происходить значительно быстрее, чем просмотр всей перестановки, потому что вы выходите из ветви раньше, когда в текущей ветви дерева нет больше букв, соответствующих ключам.
Учитывая созданное выше дерево и список из 3000 общих слов, он быстро находит 100 слов, рекурсивно сканируя дерево. Подсчет количества раз, когда внутренний цикл запускает цикл, показывает 2670
со словарем из 3000 слов - не так уж и плохо, учитывая, что в палиндроме есть 3,6 миллиона перестановок букв. Использование намного большего списка слов в /usr/share/dict/words
, который содержит примерно 250 000 слов, требует 21543 циклов. Поиск «лоскутное одеяло» с большим списком запускался только 100 раз.
Вот код Python для обхода дерева для каждой допустимой перестановки.
def findWords(word, trie = trie, cur = '', words = []):
for i, letter in enumerate(word):
if letter in trie:
if 'word' in trie[letter]:
words.append(cur + letter)
findWords(word[:i] + word[i+1:], trie[letter], cur+letter, words )
return words
words = findWords("palindrome")
Результат:
['palm',
'pale',
'pain',
'pair',
'pan',
'panel',
'plan',
...
'media',
'ear',
'earn',
'end',
'era']