Algorithm Search⌘K

Proofs: LCP interval tree

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

The stack scan builds the lcp-interval tree full

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

Notation. T=T[0…n−1]T = T[0 \ldots n-1], 0-based, no sentinel; a proper prefix sorts first. Sr=TSA[r]S_r = T_{SA[r]} is the suffix of rank rr, LCP[r]=lcp(Sr−1,Sr)LCP[r] = \mathrm{lcp}(S_{r-1}, S_r) for 1≤r<n1 \le r < n, and for the definitions LCP[0]=LCP[n]=−1LCP[0] = LCP[n] = -1. The end of a suffix counts as a symbol smaller than all others, so two different suffixes always differ at some depth. By the LCP-RMQ lemma, lcp(Sa,Sb)=min⁡LCP[a+1…b]\mathrm{lcp}(S_a, S_b) = \min LCP[a+1 \ldots b] for a<ba < b. An ℓ\ell-interval and its ℓ\ell-indices are as on the page; for an interval vv, k1(v)k_1(v) is its first ℓ\ell-index. The root is [0,n−1][0, n-1] with value 0.

Lemma 1 (intervals are branching substrings). For ℓ>0\ell > 0, [i,j][i, j] is an ℓ\ell-interval if and only if it is the set of ranks of the suffixes that start with some uu of length ℓ\ell that is followed in TT by two different symbols (the end counts). In particular two lcp-intervals are nested or disjoint, and two different ℓ\ell-intervals with the same ℓ\ell are disjoint.

Proof. The suffixes that start with a given uu have consecutive ranks. (⇒\Rightarrow) By condition 2 and the lemma, all SrS_r with i≤r≤ji \le r \le j start with the same uu of length ℓ\ell, and at an ℓ\ell-index kk, Sk−1S_{k-1} and SkS_k differ at depth ℓ\ell. By conditions 1 and 3, Si−1S_{i-1} and Sj+1S_{j+1}, if they exist, do not start with uu. (⇐\Leftarrow) Let [i,j][i, j] be the ranks of the suffixes that start with uu; two of them differ at depth ℓ\ell, so i<ji < j, all of LCP[i+1…j]LCP[i+1 \ldots j] are at least ℓ\ell, and the minimum between those two is exactly ℓ\ell. LCP[i]<ℓLCP[i] < \ell and LCP[j+1]<ℓLCP[j+1] < \ell because the neighbors do not start with uu. The last claim follows: for uu and u′u', the sets are nested if one is a prefix of the other and disjoint otherwise. □\square

Lemma 2 (children). Let v=[i,j]v = [i, j] have value ℓ\ell and ℓ\ell-indices k1<⋯<krk_1 < \cdots < k_r (for the root, the 0-indices, possibly none). Each range [i,k1−1],…,[kr,j][i, k_1 - 1], \ldots, [k_r, j] is a single rank or an lcp-interval of value larger than ℓ\ell, and every lcp-interval strictly inside vv lies inside one of them. So the lcp-intervals form a tree under containment, and these ranges are the children.

Proof. In a range [a,b][a, b] with a<ba < b, the values LCP[a+1…b]LCP[a+1 \ldots b] exceed ℓ\ell; let ℓ′\ell' be their minimum. LCP[a]LCP[a] is LCP[i]<ℓLCP[i] < \ell or an ℓ\ell-index value ℓ\ell, and LCP[b+1]LCP[b+1] is an ℓ\ell-index value or LCP[j+1]<ℓLCP[j+1] < \ell, so both are below ℓ′\ell': [a,b][a, b] is an ℓ′\ell'-interval. An interval w=[a′,b′]⊊vw = [a', b'] \subsetneq v of value ℓ′′\ell'' cannot have an ℓ\ell-index kk in (a′,b′](a', b']: then ℓ′′≤ℓ\ell'' \le \ell, and LCP[a′]<ℓ′′≤ℓLCP[a'] < \ell'' \le \ell forces a′=ia' = i, and in the same way b′=jb' = j, so w=vw = v. □\square

Invariant. At the start of the iteration with index ii (1≤i≤n1 \le i \le n), the stack holds, from the bottom, the root and the lcp-intervals vv with k1(v)≤i−1≤rb(v)k_1(v) \le i - 1 \le rb(v), in order of increasing value, each with its correct lblb; and kids(v)kids(v) lists, in rank order, the child intervals cc of vv with rb(c)≤i−2rb(c) \le i - 2. These intervals all contain rank i−1i - 1, so by Lemma 1 they are nested, and their values are different, which gives the strict order.

For i=1i = 1, no interval has k1≤0k_1 \le 0, the stack is the root, and no lists are filled. Assume the invariant at ii, and let h=LCP[i]h = LCP[i] for i<ni < n and h=0h = 0 for i=ni = n. For an interval other than the root, h=0h = 0 acts as LCP[n]=−1LCP[n] = -1, since all their values are positive.

Step 1 (pops). An interval on the stack contains i−1i - 1. If its value is greater than hh, it does not contain ii (else LCP[i]LCP[i] would be at least its value), so rb=i−1rb = i - 1, which line L5 records. If its value is at most hh, it contains ii, since rb=i−1rb = i - 1 would need LCP[i]LCP[i] below its value. So L4 and L5 pop exactly the intervals that end at i−1i - 1, and the others still satisfy k1≤i≤rbk_1 \le i \le rb.

Step 2 (push). The intervals with k1(v)=ik_1(v) = i have value h=LCP[i]h = LCP[i]; there is at most one, the hh-interval H=[a,b]H = [a, b] that contains ii, and only if k1(H)=ik_1(H) = i. Line L7 pushes if and only if this HH exists. If k1(H)=ik_1(H) = i, HH was not on the stack, and the top after the pops contains ii with a value at most hh; it is not HH, so its value is less than hh. Conversely, if the top has a value less than hh and h>0h > 0, the hh-interval that contains i−1i - 1 and ii is not on the stack (it would lie above the top and not be popped), so its first hh-index is ii.

Its left end is correct. Here a=max⁡{a′<i:LCP[a′]<h}a = \max \{ a' < i : LCP[a'] < h \}. If a=i−1a = i - 1, then LCP[i−1]<hLCP[i-1] < h. A popped interval QQ ends at i−1i - 1 and starts before it, so LCP[i−1]≥value(Q)>hLCP[i-1] \ge value(Q) > h; hence nothing is popped, and lb=i−1lb = i - 1 from line L3. If a<i−1a < i - 1, then LCP[a+1…i−1]>hLCP[a+1 \ldots i-1] > h, since k1(H)=ik_1(H) = i; with gg their minimum, G=[a,i−1]G = [a, i - 1] is a gg-interval with k1(G)≤i−1k_1(G) \le i - 1, on the stack and popped. The last interval PP popped has the smallest value among the popped ones, so G⊆PG \subseteq P; and P⊊HP \subsetneq H, as both contain i−1i - 1 and PP has the larger value. So a≤lb(P)≤lb(G)=aa \le lb(P) \le lb(G) = a, and line L5 left lb=lb(P)=alb = lb(P) = a.

Step 3 (children). Let cc be popped, and pp its parent in the tree of Lemma 2. Then cc is the child range of pp that ends at i−1i - 1: either rb(p)=i−1rb(p) = i - 1, or ii is a value(p)value(p)-index and h=value(p)h = value(p). If pp is on the stack, it is the interval just below cc, since the stack is a chain under containment; and h≤value(p)h \le value(p) in both cases (in the first, pp ends at i−1i - 1 and is popped next, or is the root at i=ni = n with h=0h = 0). Line L6 appends cc to kids(p)kids(p). If pp is not on the stack, then k1(p)>i−1k_1(p) > i - 1; pp has an ℓ\ell-index before every child that is not its first, so cc is its first child and k1(p)=ik_1(p) = i, that is, p=Hp = H. The interval below cc is then a proper ancestor of HH with a value less than hh, so line L6 keeps cc as lastlast, the loop stops, and line L8 makes cc the first child of HH. In all cases cc joins its parent's list after the children that closed earlier, so the lists are in rank order.

So the invariant holds at i+1i + 1. At i=ni = n, every interval other than the root is popped and none is pushed, and line L9 sets rb(root)=n−1rb(root) = n - 1. By Lemma 2 and the invariant, each lcp-interval was pushed once, at i=k1i = k_1, closed with its correct ends, and attached to its parent. For n≤1n \le 1 the loop pushes nothing and the tree is the root.

Theorem (time). The build runs in O(n)O(n) time, and there are at most n−1n - 1 intervals besides the root.

Proof. Each iteration pushes at most one interval, at an index i≤n−1i \le n - 1, so there are at most n−1n - 1 pushes, at most as many pops, and O(1)O(1) work besides. The loop has nn iterations, so it ends. ■\blacksquare

Back to LCP interval tree