Purpose: Build a suffix array in time using induced sorting. It is both the asymptotically optimal and, in practice, the fastest general-purpose suffix array construction.
Algorithm
- Append a sentinel
$smaller than every character. - Classify each suffix as S-type (smaller than the suffix to its right) or L-type (larger). One right-to-left pass:
type[i] = (s[i] < s[i+1]) || (s[i] == s[i+1] && type[i+1] == S). - Mark LMS positions β S-type positions whose left neighbour is L-type. The LMS substrings (from one LMS position up to and including the next) tile the string.
- Induced sort from a set of correctly placed LMS suffixes:
- bucket suffixes by first character;
- place the LMS suffixes at the ends of their buckets;
- left-to-right pass: for each placed suffix , if is L-type, put it at the current head of its bucket;
- right-to-left pass: for each placed suffix , if is S-type, put it at the current tail of its bucket.
- Use step 4 with LMS positions in arbitrary order to obtain the correct relative order of LMS substrings. Name them, forming a reduced string of length .
- If all names are distinct, the reduced suffix array is immediate; otherwise recurse on the reduced string.
- Map the reduced suffix array back to true LMS positions and run the induced sort (step 4) one final time. That is the suffix array.
Complexity
- Time:
- Space: integers (plus bucket counters)
Why It Works
The two induced passes rely on a simple ordering fact: within one character bucket, all L-type suffixes precede all S-type suffixes. Given that, if suffix is already in its final position, suffix βs position relative to other suffixes starting with the same character is fully determined by whether it is L or S, and by the order of the suffixes that follow. The passes therefore induce the full order from a correctly sorted subset β and the LMS suffixes are a subset small enough (at most ) to make the recursion linear by the geometric series.
Comparison
| Method | Time | Practical speed | Code length |
|---|---|---|---|
| Naive sort of suffixes | terrible | 3 lines | |
Prefix doubling + sort | fine to | ~20 lines | |
| Prefix doubling + radix | good to | ~35 lines | |
| DC3 / skew | good | ~60 lines | |
| SA-IS | best | ~90 lines |
In a contest
prefix doubling with radix sort is almost always enough, and it is far less error-prone to write under time pressure. Keep SA-IS in your template library rather than in your head.
Variants / Use Cases
- Kasai β pair with SA-IS for the LCP array in
- Burrows-Wheeler transform and FM-index β SA-IS is the standard construction step
- Bioinformatics β genome indexing is the reason linear-time SA construction is a solved engineering problem
- Suffix automaton β a different linear structure; often easier to code and covers most contest use cases
- Farachβs algorithm β the linear-time suffix tree analogue that inspired induced sorting