From 43c25ebcc7889c4364a31f075af44b391b011449 Mon Sep 17 00:00:00 2001 From: santhosh vaiyapuri Date: Sat, 2 Feb 2019 19:37:12 +0100 Subject: [PATCH] Removed usage of prefix in a node, just a key is enough --- Trie/Trie.ts | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/Trie/Trie.ts b/Trie/Trie.ts index 886f92b6..a1b1c875 100644 --- a/Trie/Trie.ts +++ b/Trie/Trie.ts @@ -1,26 +1,24 @@ +// represents the leaf node's key from its parent, meaning thats the end of a word +const LEAF_NODE_NOTATION = "$" + class Node { edges: Map = new Map() - prefix: string isWordEnd: boolean = false - constructor(prefix = "") { - this.prefix = prefix - } - // adds the char to the edge if not present, and returns the edge // or returns the edge if already present addChar = (char: string): Node => { if (this.edges.has(char)) { return this.edges.get(char)! } - const node = new Node(this.prefix + char) + const node = new Node() this.edges.set(char, node) return node } addLeaf = (): Node => { - const node = new Node(this.prefix) - this.edges.set(this.prefix, node) + const node = new Node() + this.edges.set(LEAF_NODE_NOTATION, node) node.isWordEnd = true return node } @@ -67,49 +65,52 @@ export default class Trie { } // walk the trie until we find the last matching char - _walkTrieForBestMatch = (word: string): { node: Node, length: number } => { + _walkTrieForBestMatch = (word: string): { node: Node, length: number, stringMatched: string } => { let currentNode: Node = this._head + let stringMatched = "" let i = 0; for (;i < word.length; i++) { const char = word.charAt(i) const node = currentNode.getEdge(char) if (node === undefined) break; else { + stringMatched = stringMatched + char currentNode = node } } // the head node has a empty char, so the length will be equal to index that we have walked so far - return { node: currentNode, length: i } + return { node: currentNode, length: i, stringMatched } } - // send empty word to accept partial matches - _walkNodeToGetWords = (node: Node, patterns: Set, word = "") => { + // send empty word to accept partial matches, parent is used for recursion + _walkNodeToGetWords = (node: Node, patterns: Set, word = "", parent = "") => { if (node.isWordEnd) { // the if check is to avoid partial matches - // for example, when we check for "partyy" we should not return "part" - if (word.length <= node.prefix.length) { - patterns.add(node.prefix) + // for example, when we check for "partyy" we should not return "part" or "party" + if (word.length <= parent.length) { + patterns.add(parent) } return } for (const [key, value] of node.edges) { // the next constructed words' predecessor must have have the last predecssor + current key // like "lo" + "r" then onto "lor" then "e" and "m" - this._walkNodeToGetWords(value, patterns, word) + const nextParent = key === LEAF_NODE_NOTATION ? parent : (parent + key) // when key is "$", dont add it to parent string + this._walkNodeToGetWords(value, patterns, word, nextParent) } } hasWord = (word: string): boolean => { const { node, length } = this._walkTrieForBestMatch(word) - const endNode = node.getEdge(word) + const endNode = node.getEdge(LEAF_NODE_NOTATION) const hasEndNode = endNode !== undefined && endNode.isWordEnd return (length === word.length && hasEndNode) } getSuggestions = (word: string, acceptPartialMatch = false): Set => { - const { node } = this._walkTrieForBestMatch(word) + const { node, stringMatched } = this._walkTrieForBestMatch(word) const patterns: Set = new Set() - this._walkNodeToGetWords(node, patterns, acceptPartialMatch ? "" : word) + this._walkNodeToGetWords(node, patterns, acceptPartialMatch ? "" : word, stringMatched) return patterns }