Algorithm Search⌘K

Proofs: Suffix tree

These are the human proofs of Suffix tree, at full length.

The naive build gives the suffix tree, and LOCUS finds exactly the substrings full

This proof shows: correctness, termination.

Let T=S$=T[0…n]T = S\$ = T[0 \ldots n], where $\$ occurs only at index nn, and Ti=T[i…n]T_i = T[i \ldots n]. For a node vv, str(v)str(v) is the concatenation of the labels from the root to vv. Say that a string xx is spelled if xx is a prefix of str(v)str(v) for some node vv; then the position where xx ends is unique, at a node or inside an edge, when (T1) holds.

Lemma 1 (no suffix is a prefix of another). For i<ji < j, TjT_j is not a prefix of TiT_i. Proof. TjT_j has its $\$ at offset n−jn - j, and TiT_i has at offset n−j<n−in - j < n - i the symbol T[i+n−j]T[i + n - j] with i+n−j<ni + n - j < n, which is not $\$. □\square

Claim. After the pass of line L1 for ii, the tree satisfies (T1), (T3), and (T2') the leaves are exactly one leaf for each i′≤ii' \le i, with str=Ti′str = T_{i'}; moreover, the spelled strings are exactly the prefixes of T0,…,TiT_0, \ldots, T_i.

Proof by induction on ii. Before the first pass the tree is the root alone; it spells only the empty string, and (T1), (T3) hold.

Consider the pass for ii. The loop keeps: T[i…j−1]=str(v)T[i \ldots j-1] = str(v), so it is spelled. At line L3, if vv has no child edge with first symbol T[j]T[j], then T[i…j]T[i \ldots j] is not spelled, and line L4 adds a leaf whose label T[j…n]T[j \ldots n] is nonempty (as j≤nj \le n), with a new first symbol; the new leaf has str=Tistr = T_i. Otherwise line L6 matches kk symbols of the label of ww. If k=b−ak = b - a, the whole label matches, T[i…j+k−1]=str(w)T[i \ldots j + k - 1] = str(w), and the loop goes on with v=wv = w. This cannot go on past j=nj = n with the label ending at nn: if str(w)str(w) equalled TiT_i for an existing node ww, then TiT_i would be a prefix of some Ti′T_{i'}, i′<ii' < i, by the induction hypothesis, which Lemma 1 forbids. So line L6 compares at most to the end of TiT_i, and when k<b−ak < b - a, the symbols T[j+k]T[j + k] and T[a+k]T[a + k] exist and differ. Line L8 splits the edge into a node uu with str(u)=T[i…j+k−1]str(u) = T[i \ldots j + k - 1] and label T[a…a+k−1]T[a \ldots a + k - 1], nonempty because the first symbols agree (k≥1k \ge 1). Then uu has two children, ww with the label T[a+k…b−1]T[a + k \ldots b - 1] and the new leaf with label T[j+k…n]T[j + k \ldots n]; their first symbols T[a+k]≠T[j+k]T[a + k] \ne T[j + k] differ.

So (T1) holds after the pass: labels are nonempty, first symbols at each node are distinct, the new inner node has two children and other nodes lose no children. (T3) holds since depths are set as the parent depth plus the label length, and the split does not change strstr of any old node. The spelled strings are the old ones plus the prefixes of TiT_i, since the new leaf extends the spelled prefix of TiT_i to all of it. The new leaf is the only new leaf, so (T2') holds. Each pass ends: jj increases by k≥1k \ge 1 in each repetition of line L7 (labels are nonempty) and stays at most nn. □\square

The final tree. After i=ni = n, (T2') is (T2). The spelled strings are the prefixes of the suffixes of TT, which are the substrings of TT. An inner node v≠rootv \ne root has two children with different first symbols c≠dc \ne d, so str(v)cstr(v)c and str(v)dstr(v)d are both substrings of TT, and str(v)str(v) is right-branching. Conversely, if xx is a substring of SS followed in TT by two different symbols c,dc, d, the paths of xcxc and xdxd separate exactly where xx ends, which by (T1) can only be at a node.

LOCUS. By (T1), at each node at most one child edge starts with a given symbol, so the walk of lines L10 to L12 follows the unique path that spells PP if PP is spelled, and returns NIL at the first symbol where no continuation exists. Since PP does not contain $\$, PP is spelled if and only if it is a prefix of some TiT_i with i<ni < n, that is, if and only if PP occurs in SS. When LOCUS returns vv, PP is a prefix of str(v)str(v) and str(parent(v))str(parent(v)) is a proper prefix of PP, so a node has PP as a prefix of its strstr if and only if it is vv or below vv. So the leaves below vv are exactly the leaves whose strstr starts with PP. By (T2), these are the suffixes TiT_i that start with PP, which are the occurrences. Excluding the leaf i=ni = n, which starts with $\$, changes nothing for a nonempty PP and gives the nn suffixes of SS for the empty PP.

Size. The tree has n+1n + 1 leaves. Each inner node other than the root has at least two children, so by counting edges, the number of inner nodes is at most the number of leaves minus 1 when the root has at least two children, which holds for n≥1n \ge 1 (it has the children $\$ and S[0]S[0]). So there are at most 2(n+1)−1=2n+12(n + 1) - 1 = 2n + 1 nodes for n≥1n \ge 1, and 2 nodes for n=0n = 0. ■\blacksquare

Back to Suffix tree