Tries · Trie Fundamentals

Trie Distinct Substrings

Inserting every suffix of a string into a trie one character at a time, counting a "distinct substring found" every time a brand-new node has to be created - since a substring is uniquely identified by its root-to-node path, and every substring of the original string appears as a prefix of at least one suffix.

  • 4 min read
  • 7 practice questions
  • 1 graded problem
On this page
  1. Description
  2. The approach
  3. A second flavor: what it costs
  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 distinct substrings does a string have? "banana" has 21 substring positions but only 15 distinct substrings. Generating every substring and hashing it works but builds O(n^2) strings of average length n/3 - O(n^3) characters. A trie shares the common parts.

This skill covers the suffix trie: insert every suffix, and count each newly created node as one new distinct substring. It also covers what that costs, which is O(n^2) nodes, and the linear-size structures that replace it on long strings.

We'll go from simple to more technical:

  1. The pattern: insert all suffixes, counting new nodes.
  2. A second flavor: what it costs, and when it stops being usable.
  3. Where suffix structures run (full-text indexes, genomics, compression).
  4. What breaks it: counting steps instead of nodes, materializing substrings, scale, the empty string.

The approach

Java

int countDistinctSubstrings(String s) {
    TrieNode root = new TrieNode();
    int count = 0;
    for (int i = 0; i < s.length(); i++) {        // insert every suffix s[i..]
        TrieNode node = root;
        for (int j = i; j < s.length(); j++) {
            int c = s.charAt(j) - 'a';
            if (node.next[c] == null) {
                node.next[c] = new TrieNode();
                count++;                           // a new node = a substring not seen before
            }
            node = node.next[c];
        }
    }
    return count;
}

Python

def count_distinct_substrings(s: str) -> int:
    root = TrieNode()
    count = 0
    for i in range(len(s)):                        # insert every suffix s[i:]
        node = root
        for c in s[i:]:
            if c not in node.next:
                node.next[c] = TrieNode()
                count += 1                         # a new node = a substring not seen before
            node = node.next[c]
    return count

Every substring is a prefix of some suffix, so inserting all suffixes puts every substring on some path - and each distinct substring on exactly one node. A substring that appears several times reuses its node, which is why counting only new nodes counts each distinct substring once. "aaaa" has 10 substring positions but only 4 distinct substrings (a, aa, aaa, aaaa).

A second flavor: what it costs

The suffix trie is O(n^2) in time and in nodes. For a random 1,000-letter string it builds about 499,000 nodes, each with its own 26-slot array - tens of megabytes. That's fine for n = 1,000 in a 256 MB sandbox - in Python, where every node is a full object with a dict of children, it's about 140 MB, still inside the limit - but the approach doesn't scale: n = 100,000 would need about 5 billion nodes. The HashSet<String> version is worse still - the 499,000 distinct substrings average about 330 characters, some 160 MB of string data.

Linear-size structures exist for exactly this reason. A suffix array (all suffixes sorted) plus the LCP array counts distinct substrings as n(n + 1)/2 - sum(LCP) in O(n log n); a suffix automaton does it in O(n).

Where you'll see it

Full-text search indexes and genome analysis use the compact descendants of the suffix trie - suffix arrays (with LCP arrays) and FM-indexes - to answer "does this pattern occur, and how often?" in time proportional to the pattern. Compression tools use related structures to find repeated substrings.

Which tool for which job?

You needUse
Distinct substrings, n up to a few thousanda suffix trie, counting new nodes
Distinct substrings, large na suffix array and LCP: n(n + 1)/2 - sum(LCP)
The same in O(n)a suffix automaton
The longest repeated substringthe largest LCP between neighboring suffixes
Does a pattern occur?walk it from the root of a suffix structure

Common mistakes

MistakeWhat happensDo this instead
Counting every steprepeated substrings counted each timecount only new nodes
A hash set of substringsO(n^3) charactersshare prefixes in a trie, or use a suffix array
A suffix trie on 10^5 charactersabout 5 billion nodessuffix array + LCP, or a suffix automaton
Counting the rootone too manythe empty substring isn't counted

Check yourself

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

1. Why does inserting all suffixes put every substring into the trie?

Show answer

Any substring s[i..j] is a prefix of the suffix starting at i, so it lies on that suffix's path from the root.

2. Why count only newly created nodes?

Show answer

Each distinct substring corresponds to exactly one node. A substring that occurs again (in a later suffix) walks through its existing node, which must not be counted a second time.

3. Why doesn't this approach scale to strings of 10^5 characters?

Show answer

It creates up to about n^2 / 2 nodes - billions for n = 10^5 - each with its own child array. Suffix arrays or suffix automata solve the same problem in O(n log n) or O(n).

Key takeaways

  • Every substring is a prefix of a suffix: insert all suffixes.
  • Count new nodes only - one per distinct substring.
  • O(n^2) nodes: about 499,000 for 1,000 random letters.
  • Large inputs need suffix arrays (+ LCP) or suffix automata.

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
Small nsuffix trie, count new nodes
Large nsuffix array + LCP
O(n)suffix automaton
Longest repeatmax LCP

Snippets worth knowing by heart

Java

for (int i = 0; i < n; i++) {                       // every suffix
    TrieNode node = root;
    for (int j = i; j < n; j++) {
        int c = s.charAt(j) - 'a';
        if (node.next[c] == null) { node.next[c] = new TrieNode(); count++; }   // new = distinct
        node = node.next[c];
    }
}

Python

distinct = n * (n + 1) // 2 - sum(lcp)               # from the suffix array's LCP

The traps

  • Counting steps, not new nodes: repeated substrings are counted each time. Count only nodes you create.
  • Materializing substrings: a hash set of them holds O(n^3) characters. Share prefixes in a trie.
  • Scale: a suffix trie has O(n^2) nodes, billions for 10^5 characters. Use a suffix array with LCP, or a suffix automaton.
  • The root isn't a substring: counting it gives one too many. Leave the empty string out.

Staff-level points, one line each

  • banana: SA [5, 3, 1, 0, 4, 2], LCP [0, 1, 3, 0, 0, 2], 21 - 6 = 15.
  • 1,000 random letters: 499,019 trie nodes, and the same count from the LCP formula.
  • The longest repeated substring is the max LCP: "ana" for banana.
  • A suffix automaton on 100,000 letters: 127,465 states for about 5 billion distinct substrings.

Say it in an interview

  • "Every substring is a prefix of a suffix, so I insert all suffixes."
  • "Each new node is one new distinct substring."
  • "For large n, n(n + 1)/2 minus the sum of LCPs from a suffix array."
  • "A suffix automaton does it in linear time."

Practice

This skill comes with 7 multiple-choice questions that tell you which misconception a wrong answer reveals and 1 graded problem. 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 Distinct Substrings free

Number of Distinct Substrings in a String

HardProCoding problemJava · Python

Given a string s, return the number of distinct (non-empty) substrings it contains.

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

Practice Trie Distinct Substrings free

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

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