




Sponsored
Sponsored
This approach utilizes a reversed Trie (prefix tree) to efficiently manage and query suffixes of input characters. By storing the reversed words in a Trie, we can match suffixes of the input stream by querying from the current character backwards. For each character queried, we move through the Trie from those characters up to a certain limit (or until no further match) to see if any of the suffixes match one of the stored words.
Time Complexity for insertion: O(N*L), where N is the number of words and L is the maximum length of a word.
Time Complexity for queries: O(Q*L) where Q is the number of queries and L is the maximum length of a word.
Space Complexity: O(N*L) for storing the words in the Trie.
1class TrieNode:
2    def __init__(self):
3        self.children = {}
4        self.is_end_of_word = False
5
6class StreamChecker:
7    def __init__(self, words):
8        self.root = TrieNode()
9        self.stream = []
10        for word in words:
11            node = self.root
12            for char in reversed(word):
13                if char not in node.children:
14                    node.children[char] = TrieNode()
15                node = node.children[char]
16            node.is_end_of_word = True
17
18    def query(self, letter):
19        self.stream.append(letter)
20        node = self.root
21        for char in reversed(self.stream):
22            if node.is_end_of_word:
23                return True
24            if char not in node.children:
25                return False
26            node = node.children[char]
27        return node.is_end_of_wordThe implementation involves creating a Trie that stores each word in reverse. When a query is made, characters are added to a list representing the current stream. We then check from the current end of the stream backwards through the Trie to determine if any suffix matches a word.
The Aho-Corasick algorithm is useful for searching multiple 'dictionary' words within a text. We build an automaton with all words and query the stream letters. This data structure enables matching of all dictionary words in parallel through a finite state machine that jumps quickly between states even upon mismatches.
Time Complexity for building: O(N*L), because we insert and construct failure links.
Time Complexity for queries: O(Q*L) leveraging finite state machine transitions.
Space Complexity: O(N*L) due to the state-space storage representing words.
1#include <vector>
2#include <queue>
3#include <unordered_map>
4using namespace std;
5
class AhoCorasick {
public:
    struct TrieNode {
        unordered_map<char, TrieNode*> children;
        TrieNode* fail = nullptr;
        bool is_end = false;
    };
    TrieNode *root = new TrieNode();
    void insert(const string &word) {
        TrieNode *node = root;
        for(char c : word) {
            if(!node->children.count(c)) {
                node->children[c] = new TrieNode();
            }
            node = node->children[c];
        }
        node->is_end = true;
    }
    void build() {
        queue<TrieNode*> q;
        root->fail = root;
        for(auto &p : root->children) {
            p.second->fail = root;
            q.push(p.second);
        }
        while(!q.empty()) {
            TrieNode *curr = q.front(); q.pop();
            for(auto &p : curr->children) {
                char c = p.first;
                TrieNode *child = p.second;
                TrieNode *fail = curr->fail;
                while(fail != root && !fail->children.count(c)) {
                    fail = fail->fail;
                }
                if(fail->children.count(c)) {
                    child->fail = fail->children[c];
                } else {
                    child->fail = root;
                }
                child->is_end |= child->fail->is_end;
                q.push(child);
            }
        }
    }
};
class StreamChecker {
    AhoCorasick ac;
    AhoCorasick::TrieNode* node;
    public:
    StreamChecker(vector<string>& words) {
        for(const string &word : words) {
            ac.insert(word);
        }
        ac.build();
        node = ac.root;
    }
    bool query(char letter) {
        while(node != ac.root && !node->children.count(letter)) {
            node = node->fail;
        }
        if(node->children.count(letter)) {
            node = node->children[letter];
        }
        return node->is_end;
    }
};This C++ solution implements the Aho-Corasick algorithm: