717 words
4 分钟minutes
Classic Problems on Suffix Arrays

It’s been a while since I last wrote a post. I recently spent some time on suffix arrays and learned a lot, so I wanted to write this up as a keepsake. Credit where it’s due — the template used below is YZH’s SAIS implementation, and the section titles and example problems come from Luo Suiqian’s 2009 IOI national team paper. Respect.

Problems on a Single String#

Longest Non-Overlapping Repeated Substring#

Sort all suffixes, then binary-search the answer. For a candidate length ll, group the sorted suffixes so that every group has lcp(height) at least ll. Within each group, track the maximum and minimum of rl(sa); if their difference exceeds ll, a non-overlapping repeated substring of length ll exists. This is O(nlogn)O(n \log n). You can also do it in O(n)O(n) by using two pointers together with a monotonic deque to maintain the max/min of rl(sa); the answer is the largest lcp(height) seen across valid groups.

Musical Theme#

code

Longest Repeated Substring Occurring at Least kk Times (Overlapping Allowed)#

The same binary-search-on-ll approach works: check whether some group with lcp(height) >= l contains at least kk suffixes. That’s O(nlogn)O(n \log n). It also runs in O(n)O(n) with two pointers by sliding a window of exactly kk consecutive suffixes.

Milk Patterns#

code

Number of Distinct Substrings#

Notice that for the suffix ranked ii-th, all substrings starting at rl[i] and ending at or before lcp[i] characters into the suffix would be double-counted. Subtracting those out, the answer is i=1n(nrl[i]+1lcp[i]),\sum_{i=1}^{n} \bigl(n - rl[i] + 1 - lcp[i]\bigr), computable in O(n)O(n).

DISUBSTR - Distinct Substrings#

code

Longest Palindromic Substring#

Reverse the string, concatenate it to the original with an unused separator in the middle, then run suffix array on the combined string. Enumerate center points (splitting into odd/even-length cases) and combine with STST-table range-min on the height array. Precomputation is O(nlogn)O(n \log n); querying is O(1)O(1), so total O(nlogn)O(n \log n). If you replace the STST table with Cartesian tree + Tarjan for O(1)O(1) RMQ with O(n)O(n) preprocessing, the whole solution becomes O(n)O(n).

Palindrome#

code

Continuous Repetition Detection#

Observation: if a string is the concatenation of kk copies of a period of length ll, then lcp(s[1],s[l+1])=nllcp(s[1], s[l+1]) = n - l. Enumerate the divisors of nn and use the STST-table to check the lcplcp; that’s O(nlogn)O(n \log n). If you notice that one endpoint of the RMQ is always fixed, you can precompute in O(n)O(n) and drop the log\log.

Power Strings#

code

Substring with Maximum Number of Continuous Repetitions#

Enumerate period length ll. For any candidate period, positions s[1],s[1+l],,s[1+xl]s[1], s[1+l], \dots, s[1+xl] must fall inside a run of length-ll repeats. For adjacent samples s[1+al]s[1 + al] and s[1+al+l]s[1 + al + l], their lcplcp gives the maximum stretch to the right; if lcplcp isn’t a multiple of ll, check whether pushing the start one step to the left adds one more copy. The complexity works out to O ⁣(i=1nni)=O(nlogn).O\!\left(\sum_{i=1}^{n} \frac{n}{i}\right) = O(n \log n).

Maximum repetition substring#

code

REPEATS - Repeats#

code

Problems on Two Strings#

Longest Common Substring#

Concatenate the two strings with an unused separator, then run suffix array on the combined string. The longest common substring must appear as the lcplcp of two adjacent suffixes that come from different original strings. O(n)O(n).

Long Long Message#

code

Freedom of Choice#

code

Count of Common Substrings of Length at Least kk#

Concatenate as above and run suffix array. For each suffix of BB, count contributions from previously seen suffixes of AA, then swap roles and do the same. A monotonic stack keeps the running sum in O(1)O(1) amortized per suffix, giving O(n)O(n) overall.

Common Substrings#

code

Problems on Multiple Strings#

Longest Substring Appearing in at Least kk Strings#

Concatenate all strings using kk distinct unused separators, then run suffix array. Binary-search the length ll: for each contiguous group with lcp >= l, check whether the group spans at least kk different source strings. O(nlogn)O(n \log n). As before, two pointers + a monotonic deque bring this down to O(n)O(n).

Life Forms#

code

Longest Substring Appearing at Least Twice Non-Overlapping in Every String#

Same concatenation trick, then binary-search ll. For each lcp >= l group, per source string track the max and min rl and check that every string has two occurrences that don’t overlap. O(nlogn)O(n \log n).

PHRASES - Relevant Phrases of Annihilation#

code

Longest Substring Appearing (or Reverse-Appearing) in Every String#

For every string, append its reverse and use 2n2n distinct unused separators between the pieces. Run suffix array on the combined string, then binary-search ll and check whether each group covers every original string. Also O(nlogn)O(n \log n).

Substrings#

code

Classic Problems on Suffix Arrays
https://jerryblack.vercel.app/posts/suffix-array-en/
作者Author
逸少( ̄^ ̄)ゞJerry Black
发布于Published at
2022-08-23
许可协议License
CC BY-NC-SA 4.0