Algorithm Search⌘K

Proofs: Kasai's algorithm

These are the human proofs of Kasai's algorithm, at full length.

Kasai's algorithm computes the LCP array in linear time 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 KASAI, 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 (KASAI). 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 KASAI 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. Line L1 takes Θ(n)\Theta(n) time. ■\blacksquare

Back to Kasai's algorithm