Algorithm Search⌘K

Proofs: PHI algorithm

These are the human proofs of PHI algorithm, at full length.

The PHI algorithm computes PLCP in text order and then the LCP array, in linear time full

This proof shows: correctness, complexity.

We use the pseudocode and the notation of the page. Write Ti=t[i…n−1]T_i = t[i \ldots n-1], with TnT_n the empty string, and << for the lexicographic order. All suffixes are distinct, so SASA is a strict order and Φ[i]\Phi[i] is the suffix immediately before TiT_i in it.

Lemma 1 (the key fact). For 0≤i<n−10 \le i < n - 1, if Φ[i]≠−1\Phi[i] \ne -1 and PLCP[i]=h≥1PLCP[i] = h \ge 1, then Φ[i+1]≠−1\Phi[i+1] \ne -1 and PLCP[i+1]≥h−1PLCP[i + 1] \ge h - 1.

Proof. Let j=Φ[i]j = \Phi[i], so Tj<TiT_j < T_i and they share their first h≥1h \ge 1 symbols. Removing the common first symbol keeps the order, so Tj+1<Ti+1T_{j+1} < T_{i+1}, and they share h−1h - 1 symbols. So Ti+1T_{i+1} has a predecessor in sorted order, Φ[i+1]≠−1\Phi[i + 1] \ne -1, and its predecessor TΦ[i+1]T_{\Phi[i+1]} satisfies Tj+1≤TΦ[i+1]<Ti+1T_{j+1} \le T_{\Phi[i+1]} < T_{i+1}. A string that lies between two strings in sorted order shares with the larger one at least their common prefix, so lcp(TΦ[i+1],Ti+1)≥lcp(Tj+1,Ti+1)=h−1lcp(T_{\Phi[i+1]}, T_{i+1}) \ge lcp(T_{j+1}, T_{i+1}) = h - 1. □\square

Invariant of lines L3 to L5. At the start of iteration ii, A[k]=PLCP[k]A[k] = PLCP[k] for k<ik < i, A[k]=Φ[k]A[k] = \Phi[k] for k≥ik \ge i, and h≤PLCP[i]h \le PLCP[i] (when Φ[i]≠−1\Phi[i] \ne -1).

At i=0i = 0, line L1 has set A=ΦA = \Phi and h=0h = 0. In iteration ii, A[i]=Φ[i]A[i] = \Phi[i] is read before it is overwritten. If it is −1-1, then PLCP[i]=0PLCP[i] = 0 by definition, which line L3 stores. Otherwise, by the invariant the first hh symbols of TiT_i and TjT_j agree, and the loop of line L4 extends hh while the next symbols agree and both suffixes continue, so it stops at h=PLCP[i]h = PLCP[i]; line L5 stores it. For iteration i+1i + 1: if Φ[i]=−1\Phi[i] = -1, hh is reset to 0, a valid lower bound. If PLCP[i]=0PLCP[i] = 0, hh stays 0. If PLCP[i]≥1PLCP[i] \ge 1, hh becomes PLCP[i]−1≤PLCP[i+1]PLCP[i] - 1 \le PLCP[i + 1] by Lemma 1. The other entries are untouched, so the invariant holds.

After the loop, A=PLCPA = PLCP. Line L6 sets LCP[r]=PLCP[SA[r]]=lcp(TΦ[SA[r]],TSA[r])=lcp(TSA[r−1],TSA[r])LCP[r] = PLCP[SA[r]] = lcp(T_{\Phi[SA[r]]}, T_{SA[r]}) = lcp(T_{SA[r-1]}, T_{SA[r]}) for r≥1r \ge 1, and LCP[0]=PLCP[SA[0]]=0LCP[0] = PLCP[SA[0]] = 0, which is the LCP array.

Time. Each iteration of line L4 increments hh. Between iterations, hh decreases by at most 1, or is reset to 0, which happens only once (at SA[0]SA[0]), from a value at most nn. Since h≤nh \le n always, the total number of increments is at most nn plus the total decrease, which is at most n−1n - 1 from the decrements plus at most nn from the reset. So line L4 runs O(n)O(n) times in total, and with the scans of lines L1, L3 and L6 the time is Θ(n)\Theta(n). ■\blacksquare

Back to PHI algorithm