Created
June 5, 2020 11:59
-
-
Save m-khooryani/c9dc722393ea9e433f128da616e9a56d to your computer and use it in GitHub Desktop.
Auto-complete using Trie
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This problem was asked by Twitter. | |
Implement an autocomplete system. | |
That is, given a query string s and a set of all possible query strings, | |
return all strings in the set that have s as a prefix. | |
For example, given the query string de and the set of | |
strings [dog, deer, deal], return [deer, deal]. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
Trie trie = new Trie(); | |
trie.Insert("dog"); | |
trie.Insert("deer"); | |
trie.Insert("deal"); | |
var foundWords = trie.Search("de"); | |
foreach (var item in foundWords) | |
{ | |
Console.WriteLine(item); // deal, deer | |
} | |
} | |
} | |
class Trie | |
{ | |
private Node root; | |
public Trie() | |
{ | |
this.root = new Node(); | |
} | |
public void Insert(string s) | |
{ | |
var node = this.root; | |
foreach (char ch in s) | |
{ | |
if (node[ch] == null) | |
{ | |
node[ch] = new Node(); | |
} | |
node = node[ch]; | |
} | |
node.IsEnd = true; | |
} | |
public IEnumerable<string> Search(string s) | |
{ | |
var node = this.root; | |
foreach (var ch in s) | |
{ | |
if (node[ch] == null) | |
{ | |
return Enumerable.Empty<string>(); | |
} | |
node = node[ch]; | |
} | |
return StringsInNode(s, node, new StringBuilder()); | |
} | |
private IEnumerable<string> StringsInNode(string prefix, Node node, StringBuilder stringBuilder) | |
{ | |
foreach (Tuple<char, Node> tuple in node) | |
{ | |
var ch = tuple.Item1; | |
var childNode = tuple.Item2; | |
if (childNode.IsEnd) | |
{ | |
// we find a string! | |
yield return prefix + stringBuilder.ToString() + ch; | |
} | |
// explore child nodes to find strings | |
stringBuilder.Append(ch); | |
foreach (var stringsInSubNodes in StringsInNode(prefix, childNode, stringBuilder)) | |
{ | |
yield return stringsInSubNodes; | |
} | |
// child nodes have been explored! | |
stringBuilder.Remove(stringBuilder.Length - 1, 1); | |
} | |
} | |
} | |
class Node : IEnumerable<Tuple<char, Node>> | |
{ | |
private Node[] nodes; | |
internal bool IsEnd { get; set; } | |
private static readonly int AlphabetSize = 'z' - 'a' + 1; | |
internal Node() | |
{ | |
this.nodes = new Node[AlphabetSize]; | |
} | |
public Node this[char ch] | |
{ | |
get | |
{ | |
return this.nodes[ch - 'a']; | |
} | |
set | |
{ | |
this.nodes[ch - 'a'] = value; | |
} | |
} | |
public IEnumerator<Tuple<char, Node>> GetEnumerator() | |
{ | |
for (int i = 0; i < this.nodes.Length; i++) | |
{ | |
if (this.nodes[i] != null) | |
{ | |
yield return new Tuple<char, Node>((char)('a' + i), this.nodes[i]); | |
} | |
} | |
} | |
IEnumerator IEnumerable.GetEnumerator() | |
{ | |
return this.GetEnumerator(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment