| 72 | |
| 73 | |
| 74 | class Dawg: |
| 75 | def __init__(self): |
| 76 | self.previous_word = "" |
| 77 | self.next_id = 0 |
| 78 | self.root = DawgNode(self) |
| 79 | |
| 80 | # Here is a list of nodes that have not been checked for duplication. |
| 81 | self.unchecked_nodes = [] |
| 82 | |
| 83 | # To deduplicate, maintain a dictionary with |
| 84 | # minimized_nodes[canonical_node] is canonical_node. |
| 85 | # Based on __hash__ and __eq__, minimized_nodes[n] is the |
| 86 | # canonical node equal to n. |
| 87 | # In other words, self.minimized_nodes[x] == x for all nodes found in |
| 88 | # the dict. |
| 89 | self.minimized_nodes = {} |
| 90 | |
| 91 | # word: value mapping |
| 92 | self.data = {} |
| 93 | # value: word mapping |
| 94 | self.inverse = {} |
| 95 | |
| 96 | def insert(self, word, value): |
| 97 | if not all(0 <= ord(c) < 128 for c in word): |
| 98 | raise ValueError("Use 7-bit ASCII characters only") |
| 99 | if word <= self.previous_word: |
| 100 | raise ValueError("Error: Words must be inserted in alphabetical order.") |
| 101 | if value in self.inverse: |
| 102 | raise ValueError(f"value {value} is duplicate, got it for word {self.inverse[value]} and now {word}") |
| 103 | |
| 104 | # find common prefix between word and previous word |
| 105 | common_prefix = 0 |
| 106 | for i in range(min(len(word), len(self.previous_word))): |
| 107 | if word[i] != self.previous_word[i]: |
| 108 | break |
| 109 | common_prefix += 1 |
| 110 | |
| 111 | # Check the unchecked_nodes for redundant nodes, proceeding from last |
| 112 | # one down to the common prefix size. Then truncate the list at that |
| 113 | # point. |
| 114 | self._minimize(common_prefix) |
| 115 | |
| 116 | self.data[word] = value |
| 117 | self.inverse[value] = word |
| 118 | |
| 119 | # add the suffix, starting from the correct node mid-way through the |
| 120 | # graph |
| 121 | if len(self.unchecked_nodes) == 0: |
| 122 | node = self.root |
| 123 | else: |
| 124 | node = self.unchecked_nodes[-1][2] |
| 125 | |
| 126 | for letter in word[common_prefix:]: |
| 127 | next_node = DawgNode(self) |
| 128 | node.edges[letter] = next_node |
| 129 | self.unchecked_nodes.append((node, letter, next_node)) |
| 130 | node = next_node |
| 131 |
no outgoing calls
searching dependent graphs…