joseville

Trie, LetterDict

Oct 12th, 2021
132
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 7.40 KB | None | 0 0
  1. # DONE
  2.  
  3. # Trie is implemented as manually nested LetterDict's, factors out common code from `search` and `startsWith` into method `_search`,
  4. # Note: `Trie.END` must be
  5. #   - a character greater than 'z' for correctness
  6. #   - must specifically be `chr(ord('z') + 1)` for time and space efficiency or if using the "Sparse" version of `LetterDict`
  7.  
  8. # # The "Compact" version of `LetterDict`
  9. # class LetterDict:
  10. #     '''A dicitionary whose keys must be lowercase letters [a-z].'''
  11. #     ORD_a = ord('a')
  12. #     def __init__(self):
  13. #         self.K = 0b0 # keys are lowercase letters [a-z]
  14. #         self.V = [] # values can be anything
  15. #     def __contains__(self, key: str) -> bool:
  16. #         o = ord(key) - self.ORD_a
  17. #         return self.K & (1 << o)
  18. #     def __getitem__(self, key: str):
  19. #         present, i = self.__i(key)
  20. #         if present:
  21. #             return self.V[i]
  22. #         raise KeyError(key)
  23. #     def __setitem__(self, key: str, val):
  24. #         present, i = self.__i(key)
  25. #         if present:
  26. #             self.V[i] = val
  27. #         else:
  28. #             self.V.insert(i, val)
  29. #     def setdefault(self, key: str, defval=None):
  30. #         present, i = self.__i(key)
  31. #         if present:
  32. #             return self.V[i]
  33. #         self.V.insert(i, defval)
  34. #         return defval
  35. #     # SIDE EFFECT: Modifies self.K
  36. #     # As such, __contains__ should not call this method
  37. #     def __i(self, key: str) -> tuple[bool, int]:
  38. #         o = ord(key) - self.ORD_a
  39. #         target = 1 << o
  40. #         present = self.K & target
  41. #         self.K |= target
  42. #         K = self.K
  43. #         i = 0
  44. #         while (K := K & (K - 1)) & target:
  45. #             i += 1
  46. #         return present, i
  47.  
  48. # The "Sparse" version of `LetterDict`
  49. class LetterDict:
  50.     '''A dicitionary whose keys must be lowercase letters [a-z].'''
  51.     NUM_LETTERS = 26 + 1 # + 1 to accomoated Trie.END = chr(ord('z') + 1)
  52.     EMPTY_VALUE = object() # Cannot be `None` because `None` is a possible value
  53.     ORD_a = ord('a')
  54.     def __init__(self):
  55.         self.K = 0b0 # keys are lowercase letters [a-z]
  56.         self.V = [self.EMPTY_VALUE] * self.NUM_LETTERS  # values can be anything
  57.     def __contains__(self, key: str) -> bool:
  58.         o = ord(key) - self.ORD_a
  59.         return self.V[o] != self.EMPTY_VALUE
  60.     def __getitem__(self, key: str):
  61.         o = ord(key) - self.ORD_a
  62.         if (val := self.V[o]) == self.EMPTY_VALUE:
  63.             raise KeyError(key)
  64.         return val
  65.     def __setitem__(self, key: str, val):
  66.         o = ord(key) - self.ORD_a
  67.         self.V[o] = val
  68.     def setdefault(self, key: str, defval=None):
  69.         o = ord(key) - self.ORD_a
  70.         if (val := self.V[o]) == self.EMPTY_VALUE:
  71.             self.V[o] = defval
  72.             return defval
  73.         return val
  74.  
  75.  
  76. class Trie:
  77.    
  78.     END = chr(ord('z') + 1)
  79.  
  80.     def __init__(self):
  81.         self.root = LetterDict()
  82.    
  83.    
  84.     def insert(self, W: str) -> None:
  85.        
  86.         # Alt 1
  87.         node = self.root
  88.         for ch in W:
  89.             node = node.setdefault(ch, LetterDict())
  90.         node[self.END] = None
  91.        
  92.         # # Alt 2
  93.         # node = self.root
  94.         # for ch in W:
  95.         #     if ch not in node:
  96.         #         node[ch] = LetterDict()
  97.         #     node = node[ch]
  98.         # node[self.END] = None
  99.        
  100.         # # Alt 3
  101.         # reduce(lambda node, ch: node.setdefault(ch, LetterDict()), W, self.root)[self.END] = None
  102.    
  103.    
  104.     def search(self, W: str) -> bool:
  105.         return self._search(W)
  106.    
  107.    
  108.     def startsWith(self, P: str) -> bool:
  109.         return self._search(P, isPrefix=True)
  110.    
  111.    
  112.     def _search(self, W: str, isPrefix: bool=False) -> bool:
  113.         node = self.root
  114.         for ch in W:
  115.             if ch not in node:
  116.                 return False
  117.             node = node[ch]
  118.         return isPrefix or self.END in node
  119.  
  120.  
  121.  
  122.  
  123. # # Manually nested dictionaries, factors out common code from `search` and `startsWith` into method `_search`.
  124. # class Trie:
  125.    
  126. #     END = '$'
  127.  
  128. #     def __init__(self):
  129. #         self.root = {}
  130.    
  131.    
  132. #     def insert(self, W: str) -> None:
  133.        
  134. #         # Alt 1
  135. #         node = self.root
  136. #         for ch in W:
  137. #             node = node.setdefault(ch, {})
  138. #         node[self.END] = None
  139.        
  140. #         # # Alt 2
  141. #         # node = self.root
  142. #         # for ch in W:
  143. #             # if ch not in node:
  144. #             #     node[ch] = {}
  145. #             # node = node[ch]
  146. #         # node[self.END] = None
  147.        
  148. #         # # Alt 3
  149. #         # reduce(lambda node, ch: node.setdefault(ch, {}), W, self.root)[self.END] = None
  150.    
  151.    
  152. #     def search(self, W: str) -> bool:
  153. #         return self._search(W)
  154.    
  155.    
  156. #     def startsWith(self, P: str) -> bool:
  157. #         return self._search(P, isPrefix=True)
  158.    
  159.    
  160. #     def _search(self, W: str, isPrefix: bool=False) -> bool:
  161. #         node = self.root
  162. #         for ch in W:
  163. #             if ch not in node:
  164. #                 return False
  165. #             node = node[ch]
  166. #         return isPrefix or self.END in node
  167.  
  168.  
  169.  
  170.  
  171. # # Manually nested dictionaries
  172. # class Trie:
  173.    
  174. #     END = '$'
  175.  
  176. #     def __init__(self):
  177. #         self.root = {}
  178.    
  179.    
  180. #     def insert(self, W: str) -> None:
  181.        
  182. #         # Alt 1
  183. #         node = self.root
  184. #         for ch in W:
  185. #             node = node.setdefault(ch, {})
  186. #         node[self.END] = None
  187.        
  188. #         # # Alt 2
  189. #         # node = self.root
  190. #         # for ch in W:
  191. #             # if ch not in node:
  192. #             #     node[ch] = {}
  193. #             # node = node[ch]
  194. #         # node[self.END] = None
  195.        
  196. #         # # Alt 3
  197. #         # reduce(lambda node, ch: node.setdefault(ch, {}), W, self.root)[self.END] = None
  198.  
  199.    
  200. #     def search(self, W: str) -> bool:
  201. #         node = self.root
  202. #         for ch in W:
  203. #             if ch not in node:
  204. #                 return False
  205. #             node = node[ch]
  206. #         return self.END in node
  207.    
  208.    
  209. #     def startsWith(self, P: str) -> bool:
  210. #         node = self.root
  211. #         for ch in P:
  212. #             if ch not in node:
  213. #                 return False
  214. #             node = node[ch]
  215. #         return True
  216.  
  217.  
  218.  
  219.  
  220. # # Automatically nested dictionaries via `tree = lambda: defaultdict(tree)`
  221. # # self.root is a defaultdict of defaultdict of defaultdict of ...
  222. # class Trie:
  223.  
  224. #     END = '$'
  225.  
  226. #     def __init__(self):
  227. #         tree = lambda: defaultdict(tree)
  228. #         self.root = tree()
  229.    
  230.  
  231. #     def insert(self, W: str) -> None:
  232.        
  233. #         # Alt 1
  234. #         node = self.root
  235. #         for ch in W:
  236. #             node = node[ch]
  237. #         node[self.END] = None
  238.        
  239. #         # # Alt 2
  240. #         # reduce(dict.__getitem__, W, self.root)[self.END] = None
  241.        
  242.  
  243. #     def search(self, W: str) -> bool:
  244. #         node = self.root
  245. #         for ch in W:
  246. #             if ch not in node:
  247. #                 return False
  248. #             node = node[ch]
  249. #         return self.END in node
  250.    
  251.    
  252. #     def startsWith(self, P: str) -> bool:
  253. #         node = self.root
  254. #         for ch in P:
  255. #             if ch not in node:
  256. #                 return False
  257. #             node = node[ch]
  258. #         return True
  259.  
  260.  
  261. # Your Trie object will be instantiated and called as such:
  262. # obj = Trie()
  263. # obj.insert(word)
  264. # param_2 = obj.search(word)
  265. # param_3 = obj.startsWith(prefix)
Advertisement
Add Comment
Please, Sign In to add comment