The compressed trie of all suffixes of a string. Every substring corresponds to a path from the root, so a huge family of questions becomes tree navigation.

Structure

  • leaves (one per suffix, with a sentinel $ to prevent a suffix being a prefix of another) and at most internal nodes.
  • Edges are labelled with substrings, stored as index pairs (l, r) into the original string — so the whole tree is space despite representing substrings.
  • Every internal node has children.
s = banana$

            root
         /   |    \
    banana$  a      na
            / \    /  \
          $  na   $   na$
              |
             ...

What it answers

QuestionMethodCost
Is a substring?walk from the root
Number of occurrences of leaves under the arrival node
All occurrence positionstraverse that subtree
Longest repeated substringdeepest internal node
Number of distinct substringssum of edge lengths
Longest common substring of two stringsgeneralised tree; deepest node with leaves from both
Longest palindromic substringgeneralised tree of and + LCA
All maximal repeatsinternal nodes with left-diverse children
Longest common extensionLCA of two leaves after preprocessing
Matching statisticsone downward walk with suffix links

Construction

AlgorithmDirectionOnlineDifficulty
Weiner (1973)right to leftprepend memory
McCreight (1976)longest to shortest suffixnohard
Ukkonen (1995)left to rightyeshard, most taught
Farach (1997)divide and conquernotheoretical
From a suffix array + LCPnoeasy — see below

Do not implement a suffix tree

In a contest, use one of:

  • Suffix automaton — 30 lines, online, and its link tree is the suffix tree of the reversed string;
  • Suffix array + LCP — less memory, and the “LCP interval tree” gives suffix-tree navigation.

Ukkonen’s algorithm is famously easy to get subtly wrong, and there is essentially no problem that requires it.

Building it from a suffix array

The suffix tree’s internal nodes correspond exactly to LCP intervals: maximal ranges of the suffix array whose minimum LCP equals the node’s string depth. A monotonic stack over the LCP array constructs this tree in :

// classic stack-based construction of the "LCP interval tree"
for (int i = 1; i <= n; i++) {
    int cur = i - 1;
    while (!st.empty() && lcp[st.top()] > lcp[i]) { /* close a node */ cur = st.top(); st.pop(); }
    if (!st.empty() && lcp[st.top()] == lcp[i]) { /* attach to the existing node */ }
    st.push(i);
}

This gives you suffix-tree structure with suffix-array memory — the practical best of both.

The three suffix structures compared

Suffix treeSuffix arraySuffix automaton
Nodes integers
Memory constanthigh ( per node)lowestmedium
Build difficultyhardesteasy at easiest
OnlineUkkonen onlynoyes
Lexicographic order✔ naturalneeds work
All substringsvia LCP intervals✔ natural

Generalised suffix tree

For a set of strings, concatenate them with distinct separators, or build the automaton with resets. Each leaf records which string it came from; a node containing leaves from every input is a common substring. This is how “longest common substring of strings” is solved in linear time.

See also: Suffix Automaton · Suffix Array · Ukkonen