Definition · Suffix array and LCP arrayFor a text s of length n, the suffix array sa is the permutation of 0, 1, ..., n − 1 that lists the starting positions of the suffixes of s in increasing lexicographic order of the suffixes. The LCP array has lcp[0] = 0 and, for r ≥ 1, lcp[r] equal to the length of the longest common prefix of the suffixes at ranks r − 1 and r.
Example · The six suffixes of bananaIn lexicographic order they are a, ana, anana, banana, na and nana, starting at 5, 3, 1, 0, 4 and 2. So sa is 5, 3, 1, 0, 4, 2. The overlaps between consecutive pairs are 0, then a between a and ana, then ana between ana and anana, then 0 between anana and banana, then 0 between banana and na, then na between na and nana. So lcp is 0, 1, 3, 0, 0, 2.
Theorem · The longest repeated substring is the largest LCP entryA string w occurs at two or more distinct positions of s if and only if some entry of the LCP array is at least the length of w. Consequently the longest repeated substring of s has length equal to the maximum entry of the LCP array.
Suppose w occurs at positions i and j with i not equal to j. Then w is a prefix of both the suffix at i and the suffix at j. Every suffix lying between them in sorted order also has w as a prefix, because lexicographic order places all strings beginning with w contiguously. So two ADJACENT suffixes in that block share w, and their LCP entry is at least the length of w.
Conversely if lcp[r] is at least the length of w and w is the prefix of that length, then w prefixes the suffixes at ranks r − 1 and r, which start at two distinct positions, so w occurs twice.
Taking w as long as possible in each direction gives the equality of lengths. On banana the maximum entry is 3, at rank 2, and the substring is ana.
□
The force of that theorem is the word ADJACENT. Without it, finding the longest repeat would mean comparing every pair of suffixes; with it, n − 1 neighbour comparisons suffice, and the LCP array has already done them.
Theorem · The number of distinct substringsThe number of distinct non-empty substrings of s is n(n + 1)/2 minus the sum of the LCP array.
Count by the rank of the suffix each substring is first attributed to. The suffix at rank r, starting at sa[r], has n − sa[r] non-empty prefixes, and each substring of s is a prefix of at least one suffix.
Attribute a substring to the LOWEST rank whose suffix has it as a prefix. The prefixes of the rank-r suffix already attributed to an earlier rank are exactly its prefixes shared with the rank-r − 1 suffix, of which there are lcp[r], because sorted order makes the previous suffix the one sharing most with it.
So rank r contributes n − sa[r] − lcp[r] new substrings. Summing over r gives sum(n − sa[r]) − sum(lcp[r]), and the first sum is n^2 − (0 + 1 + ... + n − 1) = n(n + 1)/2.
□
On banana that is 1 + 2 + 2 + 6 + 2 + 2 = 15, and 21 − 6 = 15 as well. The panel prints both routes and they are the same integer, which is the check that the identity was used rather than believed.
Two doubling rounds, watched
Round one sorts the six positions by their first character, giving 5, 1, 3, 0, 2, 4 — the three positions starting a first, then b, then the two starting n, each group in whatever order the ranks left them. Round two sorts by pairs of round-one ranks at distance 1, which is enough to separate every suffix, and produces 5, 3, 1, 0, 4, 2. Two rounds, 21 comparisons, and the result agrees with sorting the six suffixes as whole strings.
text n rounds comparisons longest repeat distinct / all
banana 6 2 21 ana (3) 15 / 21
mississippi 11 3 66 issi (4) 53 / 66
abracadabra 11 3 70 abra (4) 54 / 66
abcdefgh 8 1 7 none (0) 36 / 36
aaaaaaaa 8 3 37 aaaaaaa (7) 8 / 36
The last two rows are the extremes of the counting identity. With every character distinct the LCP array is all zeros and every one of the 36 substrings is distinct; with one character repeated the LCP array is the staircase 0, 1, 2, ..., 7 summing to 28, and 36 − 28 = 8, which is n — the only distinct substrings are the runs of length 1 to 8.
The round column is the one to read against a bound. log2 n is about 2.6 for banana and about 3.5 for mississippi, and the measured rounds are 2 and 3: the construction stops as soon as every rank is distinct, which can be sooner than the ceiling. abcdefgh needs one round, because a single character already separates all eight suffixes.
What is checked against what
The suffix array is checked against sorting the suffixes as whole strings with string comparison, which is the definition and costs O(n^2 log n). The LCP array is checked against comparing each adjacent pair character by character, which is the definition and costs O(n^2). The fast constructions — prefix doubling and Kasai's one pass — are the ones whose correctness is not obvious, and they are the ones being checked.
Kasai's pass deserves the scrutiny in particular, because its whole idea is that the LCP of a suffix can be computed from the LCP of the suffix one position earlier in the TEXT, which loses at most one character. That is an argument about a quantity carried between iterations, and a carried quantity is the shape of error that no later step corrects — the same reason the rolling hash on an earlier page is recomputed from scratch.