Algorithm Search⌘K

Proofs: Compressed suffix array

These are the human proofs of Compressed suffix array, at full length.

Lookup, extraction and search on the compressed suffix array are correct full

This proof shows: correctness, termination, time.worst.

Notation. T=T[0…n−1]T = T[0 \ldots n-1] has symbols coded 1,…,σ1, \ldots, \sigma; T′=T$T' = T\$ with $=0\$ = 0 unique and smallest. Positions and rows are 0-based, Tp′=T′[p…n]T'_p = T'[p \ldots n], SASA is the suffix array of T′T' and ISAISA its inverse. F(i)=T′[SA[i]]F(i) = T'[SA[i]], Ψ(i)=ISA[(SA[i]+1) mod (n+1)]\Psi(i) = ISA[(SA[i] + 1) \bmod (n + 1)] and V(i)=F(i)(n+1)+Ψ(i)V(i) = F(i)(n + 1) + \Psi(i). A position is sampled if it is a multiple of ss or equals nn. Since $\$ is unique and smallest, different suffixes of T′T' differ at some position, and none is a prefix of another.

Lemma 1 (Ψ\Psi steps). If SA[i]<nSA[i] < n, then SA[Ψ(i)]=SA[i]+1SA[\Psi(i)] = SA[i] + 1. So for 0≤j≤n−SA[i]0 \le j \le n - SA[i], F(Ψj(i))=T′[SA[i]+j]F(\Psi^j(i)) = T'[SA[i] + j].

Proof. The first claim is the definition, since SA[i]+1≤nSA[i] + 1 \le n. The second follows by induction on jj. □\square

Lemma 2 (monotone blocks). If i<ji < j and F(i)=F(j)=c≠$F(i) = F(j) = c \ne \$, then Ψ(i)<Ψ(j)\Psi(i) < \Psi(j). Hence VV is strictly increasing over the rows 0,…,n0, \ldots, n.

Proof. TSA[i]′=c TSA[i]+1′T'_{SA[i]} = c\,T'_{SA[i]+1} and TSA[j]′=c TSA[j]+1′T'_{SA[j]} = c\,T'_{SA[j]+1}, and the first is smaller, so TSA[i]+1′<TSA[j]+1′T'_{SA[i]+1} < T'_{SA[j]+1}: their rows satisfy Ψ(i)<Ψ(j)\Psi(i) < \Psi(j). The symbol $\$ is at row 0 only. Since FF is nondecreasing in ii (the suffixes are sorted by their first symbol first) and 0≤Ψ(i)≤n0 \le \Psi(i) \le n, the value F(i)(n+1)+Ψ(i)F(i)(n+1) + \Psi(i) increases strictly: within a block by the first part, and across blocks because FF grows by at least 1, which adds at least n+1>Ψ(i)−Ψ(i′)n + 1 > \Psi(i) - \Psi(i'). □\square

So all gaps V(i)−V(i−1)V(i) - V(i-1) are positive and have gamma codes, and decoding the sample of the block of ii plus i mod bi \bmod b gaps gives V(i)V(i), from which Ψ(i)=V(i) mod (n+1)\Psi(i) = V(i) \bmod (n + 1) and F(i)=⌊V(i)/(n+1)⌋F(i) = \lfloor V(i)/(n+1) \rfloor.

Theorem 1 (lookup). CSA-LOOKUP(i)(i) returns SA[i]SA[i] after at most s−1s - 1 steps of Ψ\Psi.

Proof. Let i0i_0 be the input row and tt the number of steps taken. Invariant: SA[i]=SA[i0]+tSA[i] = SA[i_0] + t. It holds with t=0t = 0. If SA[i]SA[i] is not sampled, then SA[i]<nSA[i] < n, because nn is sampled, so by Lemma 1 the step i←Ψ(i)i \gets \Psi(i) gives SA[Ψ(i)]=SA[i]+1SA[\Psi(i)] = SA[i] + 1, and the invariant holds with t+1t + 1. The positions SA[i0],SA[i0]+1,…SA[i_0], SA[i_0] + 1, \ldots reach a multiple of ss or nn within s−1s - 1 steps, so the loop stops after at most s−1s - 1 steps. At that point the stored sample is SA[i]SA[i], and the result SA[i]−tSA[i] - t equals SA[i0]SA[i_0]. □\square

Theorem 2 (inverse and extract). inverse(p) returns ISA[p]ISA[p], and extract(l, r) returns T[l…r−1]T[l \ldots r-1].

Proof. Let q=⌊p/s⌋s≤pq = \lfloor p/s \rfloor s \le p. The stored row is ISA[q]ISA[q], and by Lemma 1 each of the p−qp - q steps moves from the row of position q+jq + j to that of q+j+1q + j + 1, as q+j<p≤nq + j < p \le n. For extract, start at the row of ll; by Lemma 1, the jj-th output is F(Ψj(ISA[l]))=T′[l+j]F(\Psi^j(ISA[l])) = T'[l + j], for j<r−lj < r - l, and l+j<r≤nl + j < r \le n, so it is a symbol of TT. □\square

Theorem 3 (search). COMPARE(i,P)(i, P) returns the sign of the comparison of TSA[i]′[0…m−1]T'_{SA[i]}[0 \ldots m-1] with PP (0 when PP is a prefix of the suffix), and CSA-FIND(P)(P) returns the rows of the suffixes that start with PP.

Proof. By Lemma 1, the loop reads T′[SA[i]],T′[SA[i]+1],…T'[SA[i]], T'[SA[i] + 1], \ldots while they equal PP. The suffix cannot run out before a difference is found, because its last symbol is $\$, which differs from every symbol of PP; so every step before a difference has SA[Ψj(i)]<nSA[\Psi^j(i)] < n and Lemma 1 applies. The first difference decides the order, and if there is none, PP is a prefix. Since the suffixes are sorted, the sign of COMPARE is nondecreasing in ii: all rows with sign −1-1, then all with 0, then all with +1+1. The first binary search finds the first row with sign at least 0, the second the first row with sign +1+1, so the range between them is the set of rows with sign 0. □\square

Time. A Ψ\Psi or FF value costs at most b−1b - 1 gamma decodes. Lookup and inverse take at most s−1s - 1 such steps; extract takes s−1+r−ls - 1 + r - l; each COMPARE takes at most mm steps, and the two binary searches O(log⁡n)O(\log n) comparisons. All loops are bounded, so every operation terminates. With a structure that gives Ψ\Psi in O(1)O(1) time, the costs are O(s)O(s), O(s+r−l)O(s + r - l) and O(mlog⁡n)O(m \log n). ■\blacksquare

Back to Compressed suffix array