ℹ️ Select 'Choose Exercise', or randomize 'Next Random Exercise' in selected language.

Choose Exercise:
Timer 00:00
WPM --
Score --
Acc --
Correct chars --

KQL: Implement a Trie for Autocomplete Suggestions

Kusto Query Language (KQL)

Goal -- WPM

Ready
Exercise Algorithm Area
1// Trie Node structure
2let _trieNode = (isEndOfWord: bool = false, children: dynamic = bag_pack_array()) {
3dynamic({'isEndOfWord': isEndOfWord, 'children': children})
4};
5
6// Function to insert a word into the Trie
7let insertWord = (root: dynamic, word: string) {
8let currentNode = root;
9let wordLength = strlen(word);
10let newRoot = root;
11
12for (let i = 0; i < wordLength; i++) {
13let char = substring(word, i, 1);
14let childIndex = -1;
15
16// Find if the child node for the current character exists
17for (let j = 0; j < array_length(currentNode.children); j++) {
18if (currentNode.children[j].key == char) {
19childIndex = j;
20break;
21}
22}
23
24// If child node doesn't exist, create it
25if (childIndex == -1) {
26let newNode = _trieNode(false, bag_pack_array());
27newRoot = set_dynamic(newRoot, 'children', bag_array_append(newRoot.children, dynamic({'key': char, 'value': newNode})));
28currentNode = newNode;
29} else {
30currentNode = currentNode.children[childIndex].value;
31}
32
33// If it's the last character, mark it as end of word
34if (i == wordLength - 1) {
35newRoot = set_dynamic(newRoot, 'isEndOfWord', true);
36}
37}
38newRoot
39};
40
41// Function to search for words with a given prefix
42let searchPrefix = (root: dynamic, prefix: string) {
43let currentNode = root;
44let prefixLength = strlen(prefix);
45let foundWords = bag_pack_array();
46
47// Traverse to the node representing the end of the prefix
48for (let i = 0; i < prefixLength; i++) {
49let char = substring(prefix, i, 1);
50let childNode = dynamic(null);
51
52for (let j = 0; j < array_length(currentNode.children); j++) {
53if (currentNode.children[j].key == char) {
54childNode = currentNode.children[j].value;
55break;
56}
57}
58
59if (childNode == null) {
60return bag_pack_array(); // Prefix not found
61}
62currentNode = childNode;
63}
64
65// Recursive helper to find all words from the prefix node
66let collectWords = (node: dynamic, currentWord: string) {
67if (node.isEndOfWord) {
68foundWords = bag_array_append(foundWords, strcat(prefix, currentWord));
69}
70
71foreach (childEntry in node.children) {
72collectWords(childEntry.value, strcat(currentWord, childEntry.key));
73}
74};
75
76collectWords(currentNode, "");
77foundWords
78};
79
80// Example Usage:
81let initialTrie = _trieNode();
82let trieAfterInsert1 = insertWord(initialTrie, "apple");
83let trieAfterInsert2 = insertWord(trieAfterInsert1, "app");
84let trieAfterInsert3 = insertWord(trieAfterInsert2, "apricot");
85let trieAfterInsert4 = insertWord(trieAfterInsert3, "banana");
86
87print "Words starting with 'ap': ", searchPrefix(trieAfterInsert4, "ap");
88print "Words starting with 'app': ", searchPrefix(trieAfterInsert4, "app");
89print "Words starting with 'ban': ", searchPrefix(trieAfterInsert4, "ban");
90print "Words starting with 'z': ", searchPrefix(trieAfterInsert4, "z");
Algorithm description viewbox

KQL: Implement a Trie for Autocomplete Suggestions

Algorithm description:

This KQL implementation demonstrates a Trie (prefix tree) data structure, commonly used for efficient string searching and autocomplete functionalities. It allows for rapid retrieval of all words that share a common prefix. A practical use case is in search engines or text editors where suggestions appear as the user types, improving user experience by reducing typing effort and guiding them to relevant results.

Algorithm explanation:

The Trie implementation consists of nodes, where each node represents a character in a string. Each node has a flag indicating if it marks the end of a word and a collection of child nodes, keyed by the next character. The `insertWord` function traverses the Trie, creating new nodes as needed for each character in the word. If a node already exists for a character, it's reused. The `searchPrefix` function navigates to the node corresponding to the end of the given prefix. From that node, a recursive helper function `collectWords` explores all descendant paths to gather all complete words that start with that prefix. The time complexity for insertion is O(L), where L is the length of the word. Prefix search is also O(P + N), where P is the length of the prefix and N is the number of words found. Space complexity is O(T), where T is the total number of characters in all inserted words. Edge cases include empty strings, prefixes not found, and words that are prefixes of other words.

Pseudocode:

TrieNode:
  isEndOfWord: boolean
  children: map (char -> TrieNode)

function insertWord(root, word):
  currentNode = root
  for each char in word:
    if char not in currentNode.children:
      create new TrieNode
      currentNode.children[char] = new TrieNode
    currentNode = currentNode.children[char]
  currentNode.isEndOfWord = true

function searchPrefix(root, prefix):
  currentNode = root
  for each char in prefix:
    if char not in currentNode.children:
      return empty list
    currentNode = currentNode.children[char]

  results = empty list
  collectWords(currentNode, prefix, results)
  return results

function collectWords(node, currentPrefix, results):
  if node.isEndOfWord:
    add currentPrefix to results
  for each char, childNode in node.children:
    collectWords(childNode, currentPrefix + char, results)