-
Notifications
You must be signed in to change notification settings - Fork 0
/
anagrams.py
76 lines (60 loc) · 2.11 KB
/
anagrams.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
from typing import Dict, Iterator
class Dictionary:
"""
Inspired by <https://stackoverflow.com/a/1924561/9621945>
"""
path: str
end: bool
parent: 'Dictionary'
children: Dict[str, 'Dictionary']
def __init__(self, path: str = '', end=False, parent=None):
self.path = path
self.end = end
self.parent = parent
self.children = {}
@staticmethod
def format(word: str):
return "".join(c for c in word.lower().strip() if c.isalpha())
@staticmethod
def remove_first_char(word: str, char: str):
remainder = [c for c in word]
remainder.remove(char)
return "".join(remainder)
def get_root(self):
return self.parent.get_root() if self.parent else self
def ingest_all(self, words: Iterator[str]):
for w in words:
self.ingest(w)
def ingest(self, word: str):
if word:
word = self.format(word)
child = self.children.get(word[0], Dictionary(path=self.path + word[0], parent=self))
child.ingest(word[1:])
self.children[word[0]] = child
else:
self.end = True
def anagrams(self, subject: str):
subject = self.format(subject)
if self.end and not subject:
# Base case 1: Perfect match
yield self.path
elif self.end:
# Base case 2: Hit end of path but still more string
for anagram in self.get_root().anagrams(subject):
yield f'{self.path} {anagram}'
# Keep recursing deeper
for c in set(subject):
if c in self.children:
yield from self.children[c].anagrams(self.remove_first_char(subject, c))
def load_dictionary():
dictionary = Dictionary()
with open('words.txt', 'r') as f:
# Obtained <https://raw.githubusercontent.com/paolino/anagrams/master/words.txt>
dictionary.ingest_all(f.readlines())
return dictionary
def main():
dictionary = load_dictionary()
for anagram in dictionary.anagrams('Liam Peck'):
print(f'- {anagram}')
if __name__ == '__main__':
main()