Algorithm Search⌘K

Proofs: LCP array

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

LCP-BUILD computes the LCP array, and the queries return the LCP of two suffixes full

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

Notation. T=T[0…n−1]T = T[0 \ldots n-1], n≥1n \ge 1, and Tp=T[p…n−1]T_p = T[p \ldots n-1] for 0≤p≤n0 \le p \le n, so TnT_n is the empty string. The order << on strings is the lexicographic order in which a proper prefix is smaller. SASA lists T0,…,Tn−1T_0, \ldots, T_{n-1} in increasing order, and RANKRANK is its inverse. For RANK[p]>0RANK[p] > 0 let pred(p)=SA[RANK[p]−1]\mathrm{pred}(p) = SA[RANK[p] - 1] and ℓ(p)=lcp(Tpred(p),Tp)\ell(p) = \mathrm{lcp}(T_{\mathrm{pred}(p)}, T_p). The claim is that after LCP-BUILD, LCP[0]=0LCP[0] = 0 and LCP[RANK[p]]=ℓ(p)LCP[RANK[p]] = \ell(p) for each pp with RANK[p]>0RANK[p] > 0.

Lemma 1 (sandwich). If x≤y≤zx \le y \le z, then lcp(x,z)≤lcp(x,y)\mathrm{lcp}(x, z) \le \mathrm{lcp}(x, y) and lcp(x,z)≤lcp(y,z)\mathrm{lcp}(x, z) \le \mathrm{lcp}(y, z).

Proof. Let k=lcp(x,z)k = \mathrm{lcp}(x, z) and c=x[0…k−1]=z[0…k−1]c = x[0 \ldots k-1] = z[0 \ldots k-1]. Suppose that yy does not start with cc, and let j<kj < k be the first index where yy and cc differ: either ∣y∣=j|y| = j, or y[j]≠c[j]y[j] \ne c[j]. If ∣y∣=j|y| = j or y[j]<c[j]y[j] < c[j], then y<xy < x, because xx starts with cc and so xx is longer than jj and agrees with yy before jj. If y[j]>c[j]y[j] > c[j], then y>zy > z for the same reason. Both contradict x≤y≤zx \le y \le z. So yy starts with cc, and both inequalities follow. □\square

Lemma 2 (drop one symbol). Let RANK[p]>0RANK[p] > 0 and ℓ(p)=h≥2\ell(p) = h \ge 2. Then p+1<np + 1 < n, RANK[p+1]>0RANK[p+1] > 0 and ℓ(p+1)≥h−1\ell(p+1) \ge h - 1.

Proof. Let q=pred(p)q = \mathrm{pred}(p). Both TqT_q and TpT_p have length at least h≥2h \ge 2 and T[q]=T[p]T[q] = T[p]. Then Tq+1T_{q+1} and Tp+1T_{p+1} are TqT_q and TpT_p without their equal first symbol. They are nonempty, they share a prefix of length h−1h - 1, and Tq+1<Tp+1T_{q+1} < T_{p+1}, because removing an equal first symbol keeps the order. So Tp+1T_{p+1} is not the smallest suffix, which gives RANK[p+1]>0RANK[p+1] > 0. The suffix Tpred(p+1)T_{\mathrm{pred}(p+1)} is the largest suffix below Tp+1T_{p+1}, so Tq+1≤Tpred(p+1)<Tp+1T_{q+1} \le T_{\mathrm{pred}(p+1)} < T_{p+1}. By Lemma 1, ℓ(p+1)=lcp(Tpred(p+1),Tp+1)≥lcp(Tq+1,Tp+1)≥h−1\ell(p+1) = \mathrm{lcp}(T_{\mathrm{pred}(p+1)}, T_{p+1}) \ge \mathrm{lcp}(T_{q+1}, T_{p+1}) \ge h - 1. □\square

Theorem 1 (LCP-BUILD). Line L1 makes RANKRANK the inverse of SASA. Then the loop of line L3 keeps this invariant at the start of round pp:

(K) For each p′<pp' < p, the entry LCP[RANK[p′]]LCP[RANK[p']] holds 00 if RANK[p′]=0RANK[p'] = 0 and ℓ(p′)\ell(p') otherwise. Also 0≤h0 \le h, and if RANK[p]>0RANK[p] > 0 then h≤ℓ(p)h \le \ell(p). If h≥1h \ge 1, then RANK[p]>0RANK[p] > 0.

Initialization. At p=0p = 0 no entry is claimed, and h=0h = 0 by line L2.

Maintenance. If RANK[p]=0RANK[p] = 0, then by (K) h=0h = 0, line L4 writes LCP[0]=0LCP[0] = 0 and keeps h=0h = 0, and h=0h = 0 satisfies (K) for p+1p + 1. Otherwise line L5 sets q=pred(p)q = \mathrm{pred}(p). Since h≤ℓ(p)h \le \ell(p), the first hh symbols of TpT_p and TqT_q are equal. Line L6 increases hh while the next symbols exist and are equal, so it stops exactly at h=ℓ(p)h = \ell(p). Line L7 writes the right value. Line L8 sets h=max⁡(ℓ(p)−1,0)h = \max(\ell(p) - 1, 0). If ℓ(p)≥2\ell(p) \ge 2, Lemma 2 gives RANK[p+1]>0RANK[p+1] > 0 and h=ℓ(p)−1≤ℓ(p+1)h = \ell(p) - 1 \le \ell(p+1). If ℓ(p)≤1\ell(p) \le 1, then h=0h = 0. In both cases (K) holds at the start of round p+1p + 1.

Termination. The loop runs for p=0,…,n−1p = 0, \ldots, n-1 and stops. Each inner loop stops, because hh grows by 1 in each step and is bounded by n−pn - p. At the end, (K) with p=np = n covers every position, and RANKRANK is a bijection onto the indices 0,…,n−10, \ldots, n-1, so every entry of LCPLCP is right. □\square

Theorem 2 (time). Line L6 increases hh at most 2n2n times in total, so LCP-BUILD takes Θ(n)\Theta(n) time.

Proof. The value hh is at most nn at all times. It decreases only at line L8, by 1, at most once in each of the nn rounds. Line L4 does not change it, because h=0h = 0 there by (K). So the number of increases is at most the final value plus the number of decreases, which is at most n+nn + n. Each round does O(1)O(1) other work, and the failed test that ends line L6 costs O(1)O(1) for each round. □\square

Lemma 3 (LCP of two ranks). For 0≤i<j<n0 \le i < j < n, lcp(TSA[i],TSA[j])=min⁡LCP[i+1…j]\mathrm{lcp}(T_{SA[i]}, T_{SA[j]}) = \min LCP[i+1 \ldots j].

Proof. Write Sk=TSA[k]S_k = T_{SA[k]} and m=min⁡LCP[i+1…j]m = \min LCP[i+1 \ldots j]. For any strings, lcp(x,z)≥min⁡(lcp(x,y),lcp(y,z))\mathrm{lcp}(x, z) \ge \min(\mathrm{lcp}(x, y), \mathrm{lcp}(y, z)), because xx and yy agree on the first lcp(x,y)\mathrm{lcp}(x, y) symbols, and yy and zz on the first lcp(y,z)\mathrm{lcp}(y, z). Applying this along Si,Si+1,…,SjS_i, S_{i+1}, \ldots, S_j gives lcp(Si,Sj)≥m\mathrm{lcp}(S_i, S_j) \ge m. For the other direction, pick kk with i<k≤ji < k \le j and LCP[k]=mLCP[k] = m. Since Si≤Sk−1<Sk≤SjS_i \le S_{k-1} < S_k \le S_j, Lemma 1 applied twice gives lcp(Si,Sj)≤lcp(Sk−1,Sj)≤lcp(Sk−1,Sk)=m\mathrm{lcp}(S_i, S_j) \le \mathrm{lcp}(S_{k-1}, S_j) \le \mathrm{lcp}(S_{k-1}, S_k) = m. □\square

Theorem 3 (queries). LCP-RANKS(i,j)(i, j) returns lcp(TSA[i],TSA[j])\mathrm{lcp}(T_{SA[i]}, T_{SA[j]}) for 0≤i<j<n0 \le i < j < n, and LCE(p,q)(p, q) returns lcp(Tp,Tq)\mathrm{lcp}(T_p, T_q) for 0≤p,q<n0 \le p, q < n.

Proof. By induction on kk, M[k][i]=min⁡LCP[i…i+2k−1]M[k][i] = \min LCP[i \ldots i + 2^k - 1]: for k=0k = 0 it is LCP[i]LCP[i], and the range of length 2k2^k is the union of the two ranges of length 2k−12^{k-1} that start at ii and at i+2k−1i + 2^{k-1}. In line L9, L=j−i≥1L = j - i \ge 1 and 2k≤L<2k+12^k \le L < 2^{k+1}. The ranges [i+1,i+2k][i+1, i+2^k] and [j−2k+1,j][j - 2^k + 1, j] lie inside [i+1,j][i+1, j], and since 2k+1≥L+12^{k+1} \ge L + 1 the second starts no later than one place after the end of the first, so together they cover [i+1,j][i+1, j]. The minimum over a union is the minimum of the two minima, so line L10 returns min⁡LCP[i+1…j]\min LCP[i+1 \ldots j], which is the claim by Lemma 3. For LCE, if p=qp = q the answer is ∣Tp∣=n−p|T_p| = n - p (line L11). Otherwise lines L12 and L13 call LCP-RANKS with the two ranks in increasing order, and TSA[RANK[p]]=TpT_{SA[RANK[p]]} = T_p. ■\blacksquare

Back to LCP array