Tries · Trie Fundamentals

Trie Count and Erase

Extending the basic trie with two integer counters per node - how many complete words end exactly here, and how many words pass through here as a prefix - so that counting and deletion become simple increments/decrements during the same root-to-node walk insert already does, without a second data structure.

  • 5 min read
  • 7 practice questions
  • 2 graded problems
On this page
  1. Description
  2. The approach
  3. A second flavor: sums with overwrites
  4. Where you'll see it
  5. Which tool for which job?
  6. Common mistakes
  7. Check yourself
  8. Key takeaways
  9. Quick revise
  10. Practice

Description

"How many stored words start with pre?" A basic trie would walk to the prefix's node and then count every word in the subtree below it - potentially the whole dictionary. Keeping the answer on the node itself, and updating it as words come and go, makes the question a walk and a read.

This skill keeps two counters on every node: how many words pass through it and how many end there. Each counter answers one of the two counting questions. Erasing mirrors inserting along the whole path, and overwritten values update the sums by their difference.

We'll go from simple to more technical:

  1. The pattern: pass and end counters, with insert and erase updating the whole path.
  2. A second flavor: Map Sum, with prefix sums that handle overwrites by adding the delta.
  3. Where per-prefix totals show up (suggestion ranking, hierarchical analytics, profilers).
  4. What breaks it: erasing only the end, mixing up pass and end, overwrites, absent words.

The approach

Java

class TrieNode {
    TrieNode[] next = new TrieNode[26];
    int pass;                                      // words passing through this node (a prefix count)
    int end;                                       // words ending exactly here
}

void insert(String word) {
    TrieNode node = root;
    for (char c : word.toCharArray()) {
        if (node.next[c - 'a'] == null) node.next[c - 'a'] = new TrieNode();
        node = node.next[c - 'a'];
        node.pass++;                               // every node on the path
    }
    node.end++;                                    // only the last node
}

void erase(String word) {                          // word is known to be present
    TrieNode node = root;
    for (char c : word.toCharArray()) {
        node = node.next[c - 'a'];
        node.pass--;                               // undo the whole path, not just the end
    }
    node.end--;
}

int countWordsEqualTo(String w)         { TrieNode n = find(w); return n == null ? 0 : n.end; }
int countWordsStartingWith(String p)    { TrieNode n = find(p); return n == null ? 0 : n.pass; }

Python

class TrieNode:
    def __init__(self):
        self.next = {}
        self.pass_count = 0                        # words passing through this node (a prefix count)
        self.end = 0                               # words ending exactly here


def insert(word: str) -> None:
    node = root
    for c in word:
        if c not in node.next:
            node.next[c] = TrieNode()
        node = node.next[c]
        node.pass_count += 1                       # every node on the path
    node.end += 1                                  # only the last node


def erase(word: str) -> None:                      # word is known to be present
    node = root
    for c in word:
        node = node.next[c]
        node.pass_count -= 1                       # undo the whole path, not just the end
    node.end -= 1


def count_words_equal_to(w: str) -> int:
    node = find(w)
    return 0 if node is None else node.end


def count_words_starting_with(p: str) -> int:
    node = find(p)
    return 0 if node is None else node.pass_count

Two counters answer two different questions: pass counts every word whose path goes through the node (so it's the prefix count), end counts only words that stop there. Erase must mirror insert along the whole path - decrementing only end leaves every prefix count too high. With duplicates allowed, end is a count, not a boolean.

A second flavor: sums with overwrites

Map Sum: insert(key, val) sets a key's value (replacing any earlier value), and sum(prefix) returns the total value of keys with that prefix. Store a running sum on each node - and when a key is overwritten, add only the change:

Java

void insert(String key, int val) {
    int delta = val - values.getOrDefault(key, 0);   // an existing key only changes by the difference
    values.put(key, val);
    TrieNode node = root;
    for (char c : key.toCharArray()) {
        if (node.next[c - 'a'] == null) node.next[c - 'a'] = new TrieNode();
        node = node.next[c - 'a'];
        node.sum += delta;                           // every prefix of key sees the change
    }
}

Python

def insert(key: str, val: int) -> None:
    delta = val - values.get(key, 0)               # an existing key only changes by the difference
    values[key] = val
    node = root
    for c in key:
        if c not in node.next:
            node.next[c] = TrieNode()
        node = node.next[c]
        node.sum += delta                          # every prefix of key sees the change

Adding the full new value instead double-counts: insert("apple", 3), then insert("apple", 2) would make sum("ap") 5 instead of 2. A side map of current values supplies the old value.

Where you'll see it

Search-suggestion systems rank completions by counts stored on trie nodes; analytics over hierarchical keys (URL paths, file paths, product categories) keep per-prefix totals so "traffic under /api/v2" is a single lookup; memory profilers aggregate allocation sizes up a call-path trie.

Which tool for which job?

You needUse
How many words start with a prefixa pass counter on every node
How many copies of an exact wordan end counter on the last node
The total value under a prefix, with overwritesnode sums plus a side map, adding val - old
The k-th word in sorted orderskip whole children by their pass counts
Top suggestions per prefix, fastcache the top few on each node
A trie that lives through many erasesprune nodes whose pass count reaches 0

Common mistakes

MistakeWhat happensDo this instead
Erase decrements only endprefix counts stay too highdecrement pass along the whole path
Using pass for exact countslonger words are counted tooend for exact, pass for prefixes
Adding the full value on overwriteapple=3, app=4, apple=2 gives sum("ap") 9 instead of 6add val - old
Erasing a word that isn't therecounts go negativecheck it exists first
Never pruning191,231 nodes for 1,480 live wordsdelete a child when its pass count hits 0

Check yourself

Try to answer before opening each one - recalling beats re-reading.

1. What's the difference between the pass and end counters?

Show answer

pass counts every word whose path goes through the node (the answer for "starts with this prefix"); end counts only words that stop exactly there (the answer for "equal to this word").

2. What goes wrong if erase decrements only the end counter?

Show answer

Every node on the erased word's path still counts it in pass, so countWordsStartingWith stays too high for all its prefixes.

3. In Map Sum, why add val - old rather than val when a key is inserted again?

Show answer

The old value is already included in the sums along the key's path. Adding the full new value leaves the old one in place too; adding the difference replaces it.

Key takeaways

  • pass (prefix count) on every node of the path; end on the last node.
  • Erase mirrors insert along the whole path.
  • Sums with overwrites add the delta, using a side map of current values.
  • Every prefix query is O(L): walk, then read a counter.

Quick revise

The whole skill on one page, for the night before an interview.

This page puts the lesson and the deep dive together. Skim it before practice or an interview, and go back to the full lesson for anything you don't recognize.

Pick the tool

You needUse
Prefix countpass
Exact countend
Prefix sums with overwritesadd val - old
k-th wordskip by pass counts
Fast suggestionscached top-k per node
Long-lived trieprune at pass 0

Snippets worth knowing by heart

Java

for (char c : word.toCharArray()) { node = node.next[c - 'a']; node.pass--; }   // erase: whole path
node.end--;

Python

delta = val - values.get(key, 0)              # Map Sum overwrite
values[key] = val

The traps

  • Erasing only end: prefix counts stay too high. Decrement pass along the whole path too.
  • pass versus end: pass counts longer words as well. Use end for exact counts, pass for prefixes.
  • Full value on overwrite: re-inserting a key adds its value twice. Add val - old.
  • Unguarded erase: erasing a missing word drives counts negative. Check it exists first.

Staff-level points, one line each

  • Pruning at pass 0: 7,562 nodes instead of 191,231 after 100,000 inserts and erases.
  • k-th word by pass counts matched sorted(words)[k - 1] for every k.
  • Top-3 cached per node matched a subtree DFS that visited 3,444 nodes for "a".
  • An unguarded erase of "apricot" left "ap" at 1 and "apr" at -1.
  • apple=3, app=4, apple=2: sum("ap") is 6, not 9.

Say it in an interview

  • "pass counts words through a node, end counts words that stop there."
  • "Erase mirrors insert along the whole path."
  • "For overwrites I add the difference, using a map of current values."
  • "When a pass count hits zero, I can prune that branch."

Practice

This skill comes with 7 multiple-choice questions that tell you which misconception a wrong answer reveals and 2 graded problems. Your answers are graded and your mastery is tracked, so you'll know when the skill holds up. Running code and saving progress need a free account.

Practice Trie Count and Erase free

Trie Implementation II (Count Words, Erase)

MediumProCoding problemJava · Python

Implement a trie supporting insert(word), countWordsEqualTo(word) (how many times exactly this word was inserted), countWordsStartingWith(prefix) (how many inserted words start with this prefix), and erase(word) (removes one occurrence of a previously inserted word).

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Map Sum Pairs

MediumProCoding problemJava · Python

Build a map from words to numbers with two operations: insert(key, val) sets a word's number, replacing any earlier one, and sum(prefix) adds up the numbers of every word that starts with prefix.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Practice Trie Count and Erase free

No card needed. Lessons, practice questions and easy problems are free forever.

Free plan, no card · 7-day Pro trial · Cancel anytime