Algorithm Search⌘K

Proofs: FM-index

These are the human proofs of FM-index, at full length.

Backward search and LF walks on the FM-index 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, and 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], and SASA is the suffix array of T′T', so the suffixes TSA[0]′<⋯<TSA[n]′T'_{SA[0]} < \cdots < T'_{SA[n]}. L[i]=T′[SA[i]−1]L[i] = T'[SA[i] - 1] when SA[i]>0SA[i] > 0 and L[i]=$L[i] = \$ otherwise. C[c]C[c] is the number of symbols of T′T' smaller than cc, and rankc(L,i)=∣{r<i:L[r]=c}∣\mathrm{rank}_c(L, i) = |\{ r < i : L[r] = c \}|. Since $\$ is unique and smallest, no suffix of T′T' is a prefix of another, and suffixes compare at the first position where they differ.

Lemma 1 (block of a symbol). The rows whose suffixes start with cc are exactly C[c],…,C[c]+nc−1C[c], \ldots, C[c] + n_c - 1, where ncn_c is the number of cc in T′T'.

Proof. The suffixes are sorted, so those that start with smaller symbols come first, and there are C[c]C[c] of them, one for each position that holds a smaller symbol. □\square

Lemma 2 (LF). Let L[i]=cL[i] = c with SA[i]=p>0SA[i] = p > 0. Then SA[C[c]+rankc(L,i)]=p−1SA[C[c] + \mathrm{rank}_c(L, i)] = p - 1.

Proof. Let i0<i1<⋯i_0 < i_1 < \cdots be the rows with L[r]=cL[r] = c, and pj=SA[ij]p_j = SA[i_j], so Tpj−1′=c Tpj′T'_{p_j - 1} = c\,T'_{p_j}. Since Tp0′<Tp1′<⋯T'_{p_0} < T'_{p_1} < \cdots, the suffixes c Tp0′<c Tp1′<⋯c\,T'_{p_0} < c\,T'_{p_1} < \cdots are in the same order. These are all the suffixes that start with cc: a suffix Tq′T'_q with T′[q]=cT'[q] = c has q<nq < n, since c≠$c \ne \$, so Tq+1′T'_{q+1} exists, and LL at its row is cc. By Lemma 1 they fill the rows C[c]C[c] onward in this order. Row i=iji = i_j has rankc(L,i)=j\mathrm{rank}_c(L, i) = j, so the suffix Tp−1′T'_{p - 1} is at row C[c]+jC[c] + j. □\square

Lemma 3 (backward step). Let QQ be a string and [sp,ep)[sp, ep) the rows whose suffixes start with QQ. For a symbol cc of TT, the rows whose suffixes start with cQcQ are [C[c]+rankc(L,sp),C[c]+rankc(L,ep))[C[c] + \mathrm{rank}_c(L, sp), C[c] + \mathrm{rank}_c(L, ep)).

Proof. A suffix Tq′T'_q starts with cQcQ if and only if T′[q]=cT'[q] = c and Tq+1′T'_{q+1} starts with QQ, that is, the row ii of Tq+1′T'_{q+1} is in [sp,ep)[sp, ep) and L[i]=cL[i] = c. By Lemma 2, the row of Tq′T'_q is C[c]+rankc(L,i)C[c] + \mathrm{rank}_c(L, i). As ii runs over the rows in [sp,ep)[sp, ep) with L[i]=cL[i] = c, rankc(L,i)\mathrm{rank}_c(L, i) takes the consecutive values rankc(L,sp),…,rankc(L,ep)−1\mathrm{rank}_c(L, sp), \ldots, \mathrm{rank}_c(L, ep) - 1. □\square

Theorem 1 (count). Lines L1 to L5 return the range of rows whose suffixes start with PP, or the empty range if PP does not occur; ep−spep - sp is the number of occurrences.

Proof. Before the loop, the range [0,n+1)[0, n + 1) is all rows, which start with the empty string. Each round applies Lemma 3 with c=P[j]c = P[j] and Q=P[j+1…m−1]Q = P[j+1 \ldots m-1]. If cc is not in TT, no suffix starts with cQcQ. Once a range is empty, all later ranges are empty, so stopping early is correct. After the loop the range is that of PP. The occurrences of a nonempty PP in TT are the suffixes of T′T' that start with PP, one row each, since PP does not contain $\$. □\square

Theorem 2 (locate). For each row ii of the range, lines L7 to L9 report SA[i]SA[i], after at most s−1s - 1 LF steps.

Proof. Invariant: SA[istart]=SA[i]+tSA[i_{\text{start}}] = SA[i] + t. It holds with t=0t = 0. If SA[i]SA[i] is not sampled, then SA[i] mod s≠0SA[i] \bmod s \ne 0, so SA[i]>0SA[i] > 0 and Lemma 2 gives SA[LF(i)]=SA[i]−1SA[\mathrm{LF}(i)] = SA[i] - 1, and tt grows by 1. The position drops by one per step and every multiple of ss, including 0, is sampled, so the loop stops after at most s−1s - 1 steps and reports SA[i]+t=SA[istart]SA[i] + t = SA[i_{\text{start}}]. □\square

Theorem 3 (extract). The extraction returns T[l…r−1]T[l \ldots r-1].

Proof. It starts at the row ii of a sampled position p≥rp \ge r, with p≤np \le n. While p>lp > l, SA[i]=p>0SA[i] = p > 0, so L[i]=T′[p−1]L[i] = T'[p - 1], and Lemma 2 moves to the row of p−1p - 1. So it reads T[p−1],T[p−2],…,T[l]T[p-1], T[p-2], \ldots, T[l], and keeps those at positions below rr. □\square

Time. The loop of L2 runs mm times with two rank queries each. Each reported occurrence takes at most s−1s - 1 LF steps, each one rank query. With checkpoints every bb rows, a rank query scans fewer than bb symbols; with a wavelet tree it takes O(log⁡σ)O(\log \sigma) time. So count is O(mb)O(m b) or O(mlog⁡σ)O(m \log \sigma), locate adds O(ksb)O(k s b) or O(kslog⁡σ)O(k s \log \sigma), and extract takes p−l<r−l+sp - l < r - l + s steps. All loops are bounded, so the operations terminate. ■\blacksquare

Back to FM-index