DSA Notes

Trie Implementation - I

Trie

The Trie data structure is used to store a set of keys represented as strings. It allows for efficient retrieval and storage of keys, making it highly effective in handling large datasets. Trie supports operations such as insertion, search, deletion of keys, and prefix searches. A Trie node is a basic element used to build a Trie, consisting of:

Key Functions for a Trie Node:
  1. Contains Key: Checks if a specific letter exists as a child node of the current Trie node.
  2. Get Child Node: Retrieves the child node corresponding to a given letter from the current Trie node.
  3. Put Child Node: Creates a link between the current node and a child node representing a specific letter.
  4. Set End Flag: Marks the current node as the end of a word by setting the end flag to true.
  5. Is End of Word: Checks if the current node is the end of a word by examining the end flag.
// Node Structure for Trie
struct Node {
    Node* links[26] = {nullptr};  // Array to store links to child nodes, each index represents a letter
    bool flag = false; // flag indicating if the node marks the end of word 

    // Check if the node contains a specific key (letter)
    bool containsKey(char ch) {
        return links[ch - 'a'] != nullptr;
    }
    // Insert a new node with a specific key into the Trie
    void put(char ch, Node* node) {
        links[ch - 'a'] = node;
    }
    // Get the node with a specific key from the Trie
    Node* get(char ch) {
        return links[ch - 'a'];
    }
    // Set the current node as the end of a word
    void setEnd() {
        flag = true;
    }
    // Check if the current node marks the end of a word
    bool isEnd() {
        return flag;
    }
};

class Trie {
public:
    Node* root;

    // Constructor to initialize the Trie with an empty root node
    Trie() {
        root = new Node();
    }
    
    // Inserts a word into the Trie in O(N)
    void insert(string word) {
        Node* node = root; // start from root node
        for (char ch : word) {
            if (!node->containsKey(ch)) {
                node->put(ch, new Node()); // Create a new node for the letter if not present
            }
            node = node->get(ch); // Move to the next node
        }
        node->setEnd(); // Mark the end of the word
    }
    
    // Returns true if the word is in the trie
    bool search(string word) {
        Node* node = root;
        for (char ch : word) {
            if (!node->containsKey(ch)) {
                return false; // letter not found 
            }
            node = node->get(ch);
        }
        return node->isEnd(); // Check if the last node marks the end of a word
    }
    
    // Returns True if there is any word in the trie that starts with the given prefix
    bool startsWith(string prefix) {
        Node* node = root;
        for (char ch : prefix) {
            if (!node->containsKey(ch)) {
                return false;
            }
            node = node->get(ch);
        }
        return true; // Prefix Found
    }
};

Trie Implementation - II

In Trie , we have to implement the following:-

struct Node {
    Node* links[26]; // child links

    int cntEndWith = 0; // Counter for no. of words that end at this node

    int cntPrefix = 0; // Counter for no. of words that have this node as a prefix

    bool containsKey(char ch) {
        return links[ch - 'a'] != nullptr;
    }
    Node* get(char ch) {
        return links[ch - 'a'];
    }
    void put(char ch, Node* node) {
        links[ch - 'a'] = node;
    }

    // Func to increment the count of words that end at this node
    void increaseEnd() {
        cntEndWith++;
    }
    // Func to increment the count of words that have this node as a prefix
    void increasePrefix() {
        cntPrefix++;
    }
    // Func to decrement the count of words that end at this node
    void deleteEnd() {
        cntEndWith--;
    }
    //Func to decrement the count of words that have this node as a prefix
    void reducePrefix() {
        cntPrefix--;
    }
};

class Trie {
public:
    Node* root;

    Trie() {
        root = new Node();
    }

    void insert(string word) {
        Node* node = root;
        for (int i = 0; i < word.size(); i++) {
            if (!node->containsKey(word[i])) {
                node->put(word[i], new Node());
            }
            node = node->get(word[i]);
            // Increment the prefix count for the node
            node->increasePrefix();
        }
        // Increment the end count for the last node of the word
        node->increaseEnd();
    }

    int countWordsEqualTo(string word) {
        Node* node = root;
        for (int i = 0; i < word.size(); i++) {
            if (node->containsKey(word[i])) {
                node = node->get(word[i]); // move to the child
            } else {
                // character is not found
                return 0;
            }
        }
        // Return the count of words ending at the node
        return node->cntEndWith;
    }

    int countWordsStartingWith(string word) {
        Node* node = root;
        for (int i = 0; i < word.size(); i++) {
            if (node->containsKey(word[i])) {
                node = node->get(word[i]);
            } else {
                return 0;
            }
        }
        // Return the count of words with the prefix
        return node->cntPrefix;
    }

    void erase(string word) {
        Node* node = root;
        for (int i = 0; i < word.size(); i++) {
            if (node->containsKey(word[i])) {
                node = node->get(word[i]);
                // Decrement the prefix count for the node
                node->reducePrefix();
            } else {
                return;
            }
        }
        // Decrement the end count for the last node of the word
        node->deleteEnd();
    }
};

Longest String with All Prefixes

struct Node {
    Node* links[26] = {nullptr};
    bool flag = false;

    bool containsKey(char ch) {
        return links[ch - 'a'] != NULL;
    }
    void put(char ch, Node* node) {
        links[ch - 'a'] = node;
    }
    Node* get(char ch) {
        return links[ch - 'a'];
    }
    void setEnd() {
        flag = true;
    }
    bool isEnd() {
        return flag;
    }
};

class Trie {
public:
    Node* root;
    
    Trie() {
        root = new Node();
    }
    
    void insert(string word){
        Node* node = root;
        for (int i = 0; i < word.size(); i++){
            if (!node->containsKey(word[i])){
                node->put(word[i], new Node());
            }
            node = node->get(word[i]);
        }
        node->setEnd();
    }
    
    bool foundAllPrefix(string word){
        Node* node = root;
        for (char ch : word){
            if (!node->containsKey(ch)) return false;
            node = node->get(ch);
            if (!node->isEnd()) return false; // prefix not marked as end
        }
        return true;
    }
};

class Solution {
public:    
    string longestValidWord(vector<string>& words) {
        Trie trie;
        for (auto it : words){
            trie.insert(it);
        }
        
        string ans = "";
        for (auto it : words){
            if (trie.foundAllPrefix(it)){
                if (it.size() > ans.size()) ans = it;
                else if (it.size() == ans.size() && it < ans) ans = it;
            }
        }
        return ans;
    }
};

Number of Distinct Substrings in a String Using Trie

struct Node {
    Node* links[26] = {nullptr};

    bool containsKey(char ch) {
        return links[ch - 'a'] != NULL;
    }
    void put(char ch, Node* node) {
        links[ch - 'a'] = node;
    }
    Node* get(char ch) {
        return links[ch - 'a'];
    }
};

class Solution {
  public:
    int countSubs(string& s) {
        int n = s.size();
        Node* root = new Node();
        int count = 0;
        for (int i = 0; i < n; i++){
            Node* node = root;
            for (int j = i; j < n; j++){
                if (!node->containsKey(s[j])){
                    node->put(s[j], new Node());
                    count++; // Increment the counter since a new substring is found
                }
                node = node->get(s[j]);
            }
        }
        return count + 1; // +1 for empty substring
    }
};

Bit PreRequisites