Implement Trie II (Prefix Tree)

medium trie design hash map

Problem

Design a prefix tree that supports duplicate words and removal. Implement a Trie class with: insert(word) to add a word (a word may be inserted more than once); countWordsEqualTo(word) to return how many times exactly that word was inserted; countWordsStartingWith(prefix) to return how many inserted words begin with the given prefix; and erase(word) to remove one previous insertion of that word. It is guaranteed that erase is only called on a word that is currently present.

Inputinsert "apple", insert "apple", countWordsEqualTo "apple", countWordsStartingWith "app", erase "apple", countWordsEqualTo "apple"
Output2, 2, 1
"apple" was inserted twice, so it counts 2 and the prefix "app" matches 2 words. After erasing one copy, exactly one "apple" remains.

class Trie:
    def __init__(self):
        self.root = {"end": 0, "pre": 0, "kids": {}}
    def insert(self, w):
        n = self.root
        for c in w:
            n["kids"].setdefault(c, {"end": 0, "pre": 0, "kids": {}})
            n = n["kids"][c]
            n["pre"] += 1
        n["end"] += 1
    def _walk(self, w):
        n = self.root
        for c in w:
            if c not in n["kids"]:
                return None
            n = n["kids"][c]
        return n
    def countWordsEqualTo(self, w):
        n = self._walk(w)
        return n["end"] if n else 0
    def countWordsStartingWith(self, p):
        n = self._walk(p)
        return n["pre"] if n else 0
    def erase(self, w):
        n = self.root
        for c in w:
            n = n["kids"][c]
            n["pre"] -= 1
        n["end"] -= 1
class Trie {
  constructor() { this.root = { end: 0, pre: 0, kids: {} }; }
  insert(w) {
    let n = this.root;
    for (const c of w) {
      if (!n.kids[c]) n.kids[c] = { end: 0, pre: 0, kids: {} };
      n = n.kids[c];
      n.pre += 1;
    }
    n.end += 1;
  }
  _walk(w) {
    let n = this.root;
    for (const c of w) {
      if (!n.kids[c]) return null;
      n = n.kids[c];
    }
    return n;
  }
  countWordsEqualTo(w) { const n = this._walk(w); return n ? n.end : 0; }
  countWordsStartingWith(p) { const n = this._walk(p); return n ? n.pre : 0; }
  erase(w) {
    let n = this.root;
    for (const c of w) { n = n.kids[c]; n.pre -= 1; }
    n.end -= 1;
  }
}
class Trie {
    private static class Node {
        int end = 0, pre = 0;
        Map<Character, Node> kids = new HashMap<>();
    }
    private Node root = new Node();
    public void insert(String w) {
        Node n = root;
        for (char c : w.toCharArray()) {
            n.kids.computeIfAbsent(c, k -> new Node());
            n = n.kids.get(c);
            n.pre += 1;
        }
        n.end += 1;
    }
    private Node walk(String w) {
        Node n = root;
        for (char c : w.toCharArray()) {
            if (!n.kids.containsKey(c)) return null;
            n = n.kids.get(c);
        }
        return n;
    }
    public int countWordsEqualTo(String w) {
        Node n = walk(w); return n != null ? n.end : 0;
    }
    public int countWordsStartingWith(String p) {
        Node n = walk(p); return n != null ? n.pre : 0;
    }
    public void erase(String w) {
        Node n = root;
        for (char c : w.toCharArray()) { n = n.kids.get(c); n.pre -= 1; }
        n.end -= 1;
    }
}
class Trie {
    struct Node {
        int end = 0, pre = 0;
        unordered_map<char, Node*> kids;
    };
    Node* root = new Node();
    Node* walk(string w) {
        Node* n = root;
        for (char c : w) {
            if (!n->kids.count(c)) return nullptr;
            n = n->kids[c];
        }
        return n;
    }
public:
    void insert(string w) {
        Node* n = root;
        for (char c : w) {
            if (!n->kids.count(c)) n->kids[c] = new Node();
            n = n->kids[c];
            n->pre += 1;
        }
        n->end += 1;
    }
    int countWordsEqualTo(string w) { Node* n = walk(w); return n ? n->end : 0; }
    int countWordsStartingWith(string p) { Node* n = walk(p); return n ? n->pre : 0; }
    void erase(string w) {
        Node* n = root;
        for (char c : w) { n = n->kids[c]; n->pre -= 1; }
        n->end -= 1;
    }
};
Time: O(L) per operation Space: O(total characters inserted)