Purpose: Compute the Lyndon factorisation of a string — its unique decomposition into non-increasing Lyndon words — in time and extra space.
Lyndon word
A non-empty string that is strictly smaller than all of its proper suffixes. Equivalently, strictly smaller than all of its non-trivial rotations.
a,ab,aab,abb,aababare Lyndon;aa,aba,baare not.Chen-Fox-Lyndon theorem: every string factors uniquely as with each Lyndon.
Algorithm
Maintain a pointer to the start of the part not yet factorised. From , run two pointers (the character being read) and (the character it is compared against, one period behind):
- Start , .
- While :
- if
s[j] > s[k]→ the candidate can still grow; reset , advance . - if
s[j] == s[k]→ still periodic; advance both and . - if
s[j] < s[k]→ the Lyndon word ends; break.
- if
- The period is . Emit copies of the Lyndon word
s[i..i+p), advancing by each time, until . - Repeat from step 1 with the new .
Code
vector<string> duval(const string& s) {
int n = s.size(), i = 0;
vector<string> factorization;
while (i < n) {
int j = i + 1, k = i;
while (j < n && s[k] <= s[j]) {
if (s[k] < s[j]) k = i; // grew: restart the period check
else k++; // equal: continue the period
j++;
}
while (i <= k) { // emit whole periods
factorization.push_back(s.substr(i, j - k));
i += j - k;
}
}
return factorization;
}Paradigm
Greedy with amortized two pointers. Each character is examined a bounded number of times across the whole run.
Complexity
- Time:
- Space: beyond the output
Why It Works
The inner loop finds the longest prefix of s[i..] that is a power of a Lyndon word: the invariant is that s[i..j) equals where s[i..i+p) is Lyndon, , and is a proper prefix of . When s[j] < s[k] the string can no longer be extended without breaking the Lyndon property, so the completed factors are exactly the copies of . Uniqueness of the factorisation is the Chen-Fox-Lyndon theorem; greedy taking of the longest Lyndon prefix at each step is exactly what produces it. ∎
Variants / Use Cases
- Smallest cyclic rotation — run Duval on and stop at the factor that starts before position ; this is the standard alternative to Booth’s algorithm
- Largest cyclic rotation — same, with the comparison reversed
- Generating all Lyndon words of length ≤ n in lexicographic order (the FKM / Duval generation algorithm) — used to build de Bruijn sequences
- Burrows-Wheeler transform and bijective BWT — built on Lyndon factorisation
- Runs / periodicity theory — the “runs theorem” proof uses Lyndon roots
- Lyndon words — the theory page