Algorithm Search⌘K

Proofs: Sparse suffix tree

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

The sparse suffix tree is the compacted trie of the chosen suffixes, and its searches report the right positions full

This proof shows: correctness, complexity.

We use the pseudocode, the notation and the invariant (P1) to (P3) of the page. Let T′=T$T' = T\$, let s1<s2<⋯<sbs_1 < s_2 < \cdots < s_b be the chosen suffixes of T′T' in lexicographic order (written by their positions), and let ht=lcp(st−1,st)h_t = lcp(s_{t-1}, s_t) for t≥2t \ge 2, h1=0h_1 = 0. Because $\$ occurs only at the end, no chosen suffix is a prefix of another.

Lemma 1 (stack). After the suffix sts_t is processed, the stack holds the path from the root to the leaf of sts_t in the compacted trie CtC_t of s1,…,sts_1, \ldots, s_t, and the tree built so far is CtC_t.

Proof. By induction on tt. For t=1t = 1 the leaf of s1s_1 is attached to the root, which is C1C_1. Let it hold for t−1t - 1. In CtC_t, the leaf of sts_t hangs from the node of depth hth_t on the path to st−1s_{t-1}: the suffixes s1,…,st−1s_1, \ldots, s_{t-1} are smaller than sts_t, and the one with the longest common prefix with sts_t among them is st−1s_{t-1}, since a string between two others in sorted order shares at least their common prefix with the larger one. So sts_t leaves the path to st−1s_{t-1} at depth hth_t, and it shares no longer prefix with any earlier suffix. Line L2 pops the nodes of depth greater than hth_t from that path. If the top has depth exactly hth_t, the leaf hangs from it. Otherwise the last popped node lies below depth hth_t on an edge from the top, and line L3 splits this edge at depth hth_t, using the representative of the popped node to spell the label, which is correct since that node's string extends the top's string along the path. Line L4 attaches the leaf with the label T′[st+ht…]T'[s_t + h_t \ldots]; its first symbol differs from st−1s_{t-1} at depth hth_t, and the later siblings, from suffixes after sts_t, are not there yet, so the children keep distinct first symbols. The nodes of the new path are on the stack. □\square

At the end the tree is CbC_b: (P1) holds by the leaves attached, (P3) holds by the labels, and (P2) holds because a node created by line L3 receives the popped node and the new leaf, and the root and other nodes only gain children. A tree with bb leaves where each internal node except the root has at least two children has at most b−1b - 1 internal nodes besides the root, so at most 2b−12b - 1 nodes besides the root.

SEARCH. By (P2) and (P3), the walk of line L5 follows the unique path that spells a prefix of PP, and it fails exactly when no node string, and thus no chosen suffix, extends a string that PP starts with, so then no chosen suffix has prefix PP. If it succeeds, the point where PP ends lies on the edge into a node xx (or at xx), and the chosen suffixes with prefix PP are exactly the leaves below xx, by (P1) and (P3). Line L6 returns them. The walk compares at most mm symbols, and the subtree of xx has fewer than 2z2z nodes for zz leaves, by the bound above, so the time is O(m+z)O(m + z).

SEARCH-ALL. Let the chosen positions be 0,k,2k,…0, k, 2k, \ldots below nn, and m≥km \ge k. Every reported position is an occurrence: if jj is a chosen position where P[r…]P[r \ldots] occurs and T[j−r…j−1]=P[0…r−1]T[j - r \ldots j - 1] = P[0 \ldots r - 1], then PP occurs at j−rj - r. Every occurrence is reported: let PP occur at ii, and let jj be the smallest multiple of kk with j≥ij \ge i, so r=j−i<k≤mr = j - i < k \le m. Then j<i+m≤nj < i + m \le n, so jj is a chosen position, P[r…]P[r \ldots] is nonempty and occurs at jj, and SEARCH reports jj for this rr; the check of line L8 succeeds, and i=j−ri = j - r is reported. No position is reported twice: a position ii is reported only for r=j−ir = j - i with jj the chosen position in [i,i+k)[i, i + k), which is unique. ■\blacksquare

Back to Sparse suffix tree