Introduction Link to heading

mplement the StreamChecker class as follows:

StreamChecker(words): Constructor, init the data structure with the given words. query(letter): returns true if and only if for some k >= 1, the last k characters queried (in order from oldest to newest, including this letter just queried) spell one of the words in the given list.

Example:

StreamChecker streamChecker = new StreamChecker(["cd","f","kl"]); // init the dictionary.
streamChecker.query('a');          // return false
streamChecker.query('b');          // return false
streamChecker.query('c');          // return false
streamChecker.query('d');          // return true, because 'cd' is in the wordlist
streamChecker.query('e');          // return false
streamChecker.query('f');          // return true, because 'f' is in the wordlist
streamChecker.query('g');          // return false
streamChecker.query('h');          // return false
streamChecker.query('i');          // return false
streamChecker.query('j');          // return false
streamChecker.query('k');          // return false
streamChecker.query('l');          // return true, because 'kl' is in the wordlist

Note:

1 <= words.length <= 2000
1 <= words[i].length <= 2000
Words will only consist of lowercase English letters.
Queries will only consist of lowercase English letters.
The number of queries is at most 40000.

Solution Link to heading

Trie solution:

const (
    alfabet = 26
)

type trie struct {
    leaf   bool
    list   []*trie
}

func newTrie(leaf bool) *trie {
    return &trie{leaf, make([]*trie, alfabet)}
}

type StreamChecker struct {
    dict    *trie
    q       []*trie
}

func Constructor(words []string) StreamChecker {
    
    var s StreamChecker
    s.dict = newTrie(false)
    s.q = []*trie{ s.dict }
    
    for _, w := range words {
        node := s.dict
        for _, l := range w {
            idx := int(l) - 'a'
            if node.list[idx] == nil {
                node.list[idx] = newTrie(false)
            }
            node = node.list[idx]
        }
        node.leaf = true
    }
    return s
}

func (s *StreamChecker) Query(letter byte) bool {
    idx := int(letter) - 'a'
    newQ := []*trie{ s.dict }
    leaf := false
    for _, parent := range s.q {
        node := parent.list[idx]
        if node != nil {
            newQ = append(newQ, node)
            leaf = leaf || node.leaf
        }
    }
    s.q = newQ
    return leaf
}

Reverse Trie Solution:

const (
    alfabet = 26
)

type trie struct {
    leaf   bool
    list   []*trie
}

func newTrie(leaf bool) *trie {
    return &trie{leaf, make([]*trie, alfabet)}
}

type StreamChecker struct {
    dict    *trie
    q       []int
    maxLen    int
}

func Constructor(words []string) StreamChecker {
    
    var s StreamChecker
    s.dict = newTrie(false)
    s.q = []int{}
    
    for _, w := range words {
        
        n := len(w)
        
        node := s.dict
        for i := n-1; i >= 0; i-- {
            idx := int(w[i]) - 'a'
            if node.list[idx] == nil {
                node.list[idx] = newTrie(false)
            }
            node = node.list[idx]            
        }

        node.leaf = true
        
        if s.maxLen < n {
            s.maxLen = n
        }
    }
    
    return s
}

func (s *StreamChecker) Query(letter byte) bool {

    idx := int(letter) - 'a'
    s.q = append(s.q, idx)
    
    n := len(s.q)
    if n > s.maxLen {
        s.q = s.q[1:]
        n--
    }    
    
    node := s.dict
    for i := n-1; i >= 0; i-- {
        node = node.list[s.q[i]]
        
        if node == nil {
            return false
        }
        if node.leaf {
            return true
        }
    }
    return false
}

Explanation Link to heading

There are two solutions, both are using trie approach. One is the forward tries, seconds one is reverse trie. Second one works faster.