This problem was asked by Twitter.

Implement an autocomplete system. That is, given a query string s and a set of all possible query strings, return all strings in the set that have s as a prefix.

For example, given the query string de and the set of strings [dog, deer, deal], return [deer, deal].

Hint: Try preprocessing the dictionary into a more efficient data structure to speed up queries.

My Solution(C++):


#include <iostream>
#include <vector>
#include <string>
#define NUM_ALPHABETS 26

struct trie{
  trie *children[NUM_ALPHABETS];
  bool isWordEnd;
};

trie *createTrieNode(){
  trie *temp = new trie;
  for (int i=0; i<NUM_ALPHABETS; i++){
    temp->children[i] = nullptr;
  }
  temp->isWordEnd = false;
  return temp;
}

bool isLastNode(trie *root){
  for (int i=0; i<NUM_ALPHABETS; i++){
    if (root->children[i]!=nullptr){
      return false;
    }
  }
  return true;
}

void getAllWords(trie *root, std::string currPrefix, std::vector<std::string> &words){
  // std::cout<<"called with prefix "<<currPrefix<<'\n';
  if (root->isWordEnd){
    words.push_back(currPrefix);
    // std::cout<<"added word "<<currPrefix<<'\n';
  }
  if (isLastNode(root)){
    return;
  }
  for (int i=0; i<NUM_ALPHABETS; i++){
    if (root->children[i]!=nullptr){
      char c = 'a'+i;
      // currPrefix.push_back(c);
      getAllWords(root->children[i], currPrefix+c, words);
    }
  }
}


class prefixTree{
private:
  int num_words;
  trie *root;
public:
  prefixTree();
  void insert(std::string key);
  bool search(std::string key);
  bool searchPrefix(std::string key);
  int getNumWords();
  std::vector<std::string> wordsWIthPrefix(std::string key);
};

prefixTree::prefixTree(){
  root = createTrieNode();
  num_words = 0;
}

void prefixTree::insert(std::string key){
  trie *crawler = this->root;
  for (char c: key){
    int idx = c-'a';
    if (crawler->children[idx]==nullptr){
      crawler->children[idx] = createTrieNode();
    }
    crawler = crawler->children[idx];
  }
  crawler->isWordEnd = true;
  this->num_words++;
}

bool prefixTree::search(std::string key){
  trie *crawler = this->root;
  for (char c: key){
    int idx = c-'a';
    if (crawler->children[idx]==nullptr){
      return false;
    }
    crawler = crawler->children[idx];
  }
  return crawler!=nullptr && crawler->isWordEnd;
}

bool prefixTree::searchPrefix(std::string key){
  trie *crawler = this->root;
  for (char c: key){
    int idx = c-'a';
    if (crawler->children[idx]==nullptr){
      return false;
    }
    crawler = crawler->children[idx];
  }
  return crawler!=nullptr;
}

int prefixTree::getNumWords(){
  return this->num_words;
}

std::vector<std::string> prefixTree::wordsWIthPrefix(std::string key){
  if (!this->searchPrefix(key)){
    return {};
  }
  if (this->search(key)){
    return {key};
  }
  std::vector<std::string> words;
  trie *crawler = this->root;
  for (char c: key){
    int idx = c-'a';
    if (crawler->children[idx]==nullptr){
      return {};
    }
    crawler = crawler->children[idx];
  }
  getAllWords(crawler, key, words);
  return words;
}

void test(){
  prefixTree pt;
  pt.insert("thus");
  pt.insert("this");
  pt.insert("there");
  pt.insert("thin");
  // pt.search("thin")?std::cout<<"thin is present"<<'\n':std::cout<<"thin is not present"<<'\n';
  // pt.search("then")?std::cout<<"then is present"<<'\n':std::cout<<"then is not present"<<'\n';
  // std::cout<<"Number of words = "<<pt.getNumWords()<<'\n';
  std::vector<std::string> words = pt.wordsWIthPrefix("thi");
  for (std::string word: words){
    std::cout<<word<<'\n';
  }

}

int main(){
  test();
  return 0;
}

My Solution(Python):

"""Leetcode version"""
class Trie:

    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.prefixes = set()
        self.words = set()

    def insert(self, word):
        """
        Inserts a word into the trie.
        :type word: str
        :rtype: void
        """
        for i in range(1, len(word)+1):
            self.prefixes.add(word[:i])
        self.words.add(word)


    def search(self, word):
        """
        Returns if the word is in the trie.
        :type word: str
        :rtype: bool
        """
        return word in self.words


    def startsWith(self, prefix):
        """
        Returns if there is any word in the trie that starts with the given prefix.
        :type prefix: str
        :rtype: bool
        """
        return prefix in self.prefixes


# Your Trie object will be instantiated and called as such:
# obj = Trie()
# obj.insert(word)
# param_2 = obj.search(word)
# param_3 = obj.startsWith(prefix)

"""Proper TRIE (I like this one more)"""
class TrieNode:
    def __init__(self, ch='*'):
        self.links = []
        self.char = ch
        self.isWord = False
        self.countPrefix = 0

    def insert(self, root, word):
        for letter in word:
            foundPrefix = False
            for link in root.links:
                if link.char == letter:
                    foundPrefix = True
                    root.countPrefix += 1
                    root = link
                    break
                else:
                    pass
            if not foundPrefix:
                newnode = TrieNode(letter)
                root.links.append(newnode)
                root = newnode
        root.isWord = True

    def search(self, root, word):
        for i, letter in enumerate(word):
            foundPrefix = False
            for link in root.links:
                if link.char == letter:
                    print(letter)
                    foundPrefix = True
                    return self.search(link, word[i+1::])
            if not foundPrefix:
                return False
        if root.isWord:
            return True
        else:
            print('Found but not a word')
            return False

    def startsWith(self, root, prefix):
        for i, letter in enumerate(prefix):
            foundPrefix = False
            for link in root.links:
                if link.char == letter:
                    print(letter)
                    foundPrefix = True
                    return self.startsWith(link, prefix[i+1:])
            if not foundPrefix:
                return False
        return True

if __name__=='__main__':
    root = TrieNode()
    root.insert(root, 'hackathon')
    print(root.search(root, 'hackathon'))
    root.insert(root, 'apple')
    print(root.search(root, 'apple'))
    print(root.search(root, 'app'))
    print(root.startsWith(root, 'app'))
    root.insert(root, 'app')
    print(root.search(root, 'app'))