How to implement text Auto-complete feature using Ternary Search Tree

Given a set of strings S and a string patt the task is to autocomplete the string patt to strings from S that have patt as a prefix, using a Ternary Search Tree. If no string matches the given prefix, print “None”.

Examples:

Input: S = {“wallstreet”, “geeksforgeeks”, “wallmart”, “walmart”, “waldomort”, “word”],
patt = “wall”
Output:
wallstreet
wallmart
Explanation:
Only two strings {“wallstreet”, “wallmart”} from S matches with the given pattern “wall”.

Input: S = {“word”, “wallstreet”, “wall”, “wallmart”, “walmart”, “waldo”, “won”}, patt = “geeks”
Output: None
Explanation:
Since none of word of set matches with pattern so empty list will be printed.

Trie Approach: Please refer this article to learn about the implementation using Trie data structure.



Ternary Search Tree Approach Follow the steps below to solve the problem:

  • Insert all the characters of the strings in S into the Ternary Search Tree based on the following conditions:
    1. If the character to be inserted is smaller than current node value, traverse the left subtree.
    2. If the character to be inserted is greater than current node value, traverse the right subtree to.
    3. If the character to be inserted is same as that of the current node value, traverse the equal subtree if it is not the end of the word. If so, mark the node as the end of the word.
  • Follow a similar approach for extracting suggestions.
  • Traverse the tree to search the given prefix patt following a similar traversal technique as mentioned above.
  • If the given prefix is not found, print “None”.
  • If the given prefix is found, traverse the tree from the node where the prefix ends. Traverse the left subtree and generate suggestions followed by the right and equal subtrees from every node .
  • Every time a node is encountered which has the endofWord variable set, it denotes that a suggestion has been obtained. Insert that suggestion into words.
  • Return words after generating all possible suggestions.

Below is the implementation of the above approach:

C++

filter_none

edit
close

play_arrow

link
brightness_4
code

// C++ Program to generate
// autocompleted texts from
// a given prefix using a
// Ternary Search Tree
#include <bits/stdc++.h>
using namespace std;
  
// Define the Node of the
// tree
struct Node {
  
    // Store the character
    // of a string
    char data;
    // Store the end of
    // word
    int end;
    // Left Subtree
    struct Node* left;
  
    // Equal Subtree
    struct Node* eq;
  
    // Right Subtree
    struct Node* right;
};
  
// Function to create a Node
Node* createNode(char newData)
{
    struct Node* newNode = new Node();
    newNode->data = newData;
    newNode->end = 0;
    newNode->left = NULL;
    newNode->eq = NULL;
    newNode->right = NULL;
    return newNode;
}
  
// Function to insert a word
// in the tree
void insert(Node** root,
            string word,
            int pos = 0)
{
  
    // Base case
    if (!(*root))
        *root = createNode(word[pos]);
  
    // If the current character is
    // less than root's data, then
    // it is inserted in the
    // left subtree
  
    if ((*root)->data > word[pos])
        insert(&((*root)->left), word,
               pos);
  
    // If current character is
    // more than root's data, then
    // it is inserted in the right
    // subtree
  
    else if ((*root)->data < word[pos])
        insert(&((*root)->right), word,
               pos);
  
    // If current character is same
    // as that of the root's data
  
    else {
        // If it is the end of word
  
        if (pos + 1 == word.size())
            // Mark it as the
            // end of word
            (*root)->end = 1;
  
        // If it is not the end of
        // the string, then the
        // current character is
        // inserted in the equal subtree
  
        else
            insert(&((*root)->eq), word, pos + 1);
    }
}
  
// Function to traverse the ternary search tree
void traverse(Node* root,
              vector<string>& ret,
              char* buff,
              int depth = 0)
{
    // Base case
    if (!root)
        return;
    // The left subtree is
    // traversed first
    traverse(root->left, ret,
             buff, depth);
  
    // Store the current character
    buff[depth] = root->data;
  
    // If the end of the string
    // is detected, store it in
    // the final ans
    if (root->end) {
        buff[depth + 1] = '\0';
        ret.push_back(string(buff));
    }
  
    // Traverse the equal subtree
    traverse(root->eq, ret,
             buff, depth + 1);
  
    // Traverse the right subtree
    traverse(root->right, ret,
             buff, depth);
}
  
// Utility function to find
// all the words
vector<string> util(Node* root,
                    string pattern)
{
    // Stores the words
    // to suggest
    char buffer[1001];
  
    vector<string> ret;
  
    traverse(root, ret, buffer);
  
    if (root->end == 1)
        ret.push_back(pattern);
    return ret;
}
  
// Function to autocomplete
// based on the given prefix
// and return the suggestions
vector<string> autocomplete(Node* root,
                            string pattern)
{
    vector<string> words;
    int pos = 0;
  
    // If pattern is empty
    // return an empty list
    if (pattern.empty())
        return words;
  
    // Iterating over the characters
    // of the pattern and find it's
    // corresponding node in the tree
  
    while (root && pos < pattern.length()) {
  
        // If current character is smaller
        if (root->data > pattern[pos])
            // Search the left subtree
            root = root->left;
  
        // current character is greater
        else if (root->data < pattern[pos])
            // Search right subtree
            root = root->right;
  
        // If current character is equal
        else if (root->data == pattern[pos])
            // Search equal subtree
            root = root->eq;
  
        // If not found
        else
            return words;
  
        pos++;
    }
  
    // Search for all the words
    // from the current node
    words = util(root, pattern);
  
    return words;
}
  
// Function to print
// suggested words
  
void print(vector<string> sugg,
           string pat)
{
    for (int i = 0; i < sugg.size();
         i++)
        cout << pat << sugg[i].c_str()
             << "\n";
}
  
// Driver Code
int main()
{
    vector<string> S
        = { "wallstreet", "geeksforgeeks",
            "wallmart", "walmart",
            "waldormort", "word" };
  
    Node* tree = NULL;
  
    // Insert the words in the
    // Ternary Search Tree
    for (string str : S)
        insert(&tree, str);
  
    string pat = "wall";
  
    vector<string> sugg
        = autocomplete(tree, pat);
  
    if (sugg.size() == 0)
        cout << "None";
  
    else
        print(sugg, pat);
  
    return 0;
}

chevron_right


Output:

wallmart
wallstreet

Time Complexity: O(L* log N) where L is length of longest word.
The space is proportional to the length of the string to be stored.

competitive-programming-img




My Personal Notes arrow_drop_up

Check out this Author's contributed articles.

If you like GeeksforGeeks and would like to contribute, you can also write an article using contribute.geeksforgeeks.org or mail your article to contribute@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.

Please Improve this article if you find anything incorrect by clicking on the "Improve Article" button below.