The string-branch view of the trie: what it is good for in string problems, and how compression turns it into a suffix tree.

Plain trie

One node per distinct prefix of the stored set. Insertion and lookup are , independent of how many words are stored. See Trie for the implementation.

Size: nodes — one per distinct prefix. Storing all suffixes of a string gives nodes, which is why plain tries do not scale to substring problems.

Compressed trie (radix tree / Patricia trie)

Merge every chain of single-child nodes into one edge labelled with a substring.

plain trie of {test, team, toast}     compressed
        t                                  t
        |                                / | \
        e --- s --- t                 e**  oast
       / \                           /  \
      a   s                        st    am
  • Node count drops from to — at most nodes for words.
  • Edge labels are stored as (start, end) index pairs into the original text, so the memory stays regardless of word length.
  • Insertion may need to split an edge, which is the only added complexity.

The compressed trie of all suffixes of a string is precisely the suffix tree.

That identity is the bridge from tries to the suffix-structure world: nodes become purely through compression.

String problems a trie solves

ProblemMethod
Autocomplete / prefix searchdescend to the prefix node, enumerate its subtree
Count words with a given prefixa cnt field per node
Longest common prefix of a setdescend while there is exactly one child
Longest prefix of that is a stored worddescend, remembering the last word-end
Word break / segmentation DPtrie walk from each position
Sort strings lexicographicallyDFS the trie — a radix sort in disguise
-th lexicographic worddescend using subtree counts
Maximum XOR pairbinary trie
Multi-pattern matchingAho-Corasick = trie + suffix links

Word break with a trie

// dp[i] = can s[0..i) be split into dictionary words?
vector<bool> dp(n + 1, false); dp[0] = true;
for (int i = 0; i < n; i++) {
    if (!dp[i]) continue;
    int v = 0;
    for (int j = i; j < n; j++) {
        v = trie.nxt[v][s[j] - 'a'];
        if (v == -1) break;
        if (trie.isEnd[v]) dp[j + 1] = true;
    }
}

worst case but with an early exit that makes it near-linear on real dictionaries — and far better than trying every substring against a hash set.

Memory: the practical constraint

Children representationLookupMemory per node
array<int, 26>104 bytes
map<char,int>~48 bytes + overhead
sorted vector<pair<char,int>>~8 bytes per child
bitmask + packed children with popcount~12 bytes

For total characters over a 26-letter alphabet, the array version is 104 MB — usually over the limit. The sorted-vector version is typically the right compromise.

The trie family

StructureIsUse
Trieprefix treedictionaries, prefixes
Compressed trietrie with merged chainsspace-efficient dictionaries
Suffix treecompressed trie of all suffixesall substrings
Binary trietrie over bitsXOR problems
Aho-Corasicktrie + failure linksmulti-pattern matching
Suffix automatona DAG, not a treeall substrings, minimal size
DAWGminimised trie of a word setdictionary compression

Persistent tries

Making a trie persistent (copy the nodes on the insertion path) gives versioned dictionaries and, for binary tries, “maximum XOR over an index range” — one of the most useful applications of persistence in competitive programming.

See also: Trie · Suffix Tree · Aho-Corasick