A Lyndon word is a non-empty string strictly smaller than every proper suffix (equivalently, than all its non-trivial rotations: it is the unique smallest rotation of itself and is aperiodic). a, ab, aab, abb, aabab, abcbc are Lyndon; aa, aba, ba, abab are not. Characterisations: (1) smaller than every proper suffix; (2) smaller than every non-trivial rotation; (3) a single letter, or with both Lyndon (standard factorisation).

Chen–Fox–Lyndon: every string factors uniquely as with each Lyndon and ; Duval computes it in time and space.

UseHow
smallest rotation (canonical form of a necklace / cyclic string / polygon)Duval on , or the two-pointer (Booth-style) scan; largest rotation: flip comparisons
de Bruijn sequence FKM: concatenate in order the Lyndon words whose length divides ; alternative: Euler circuit in the de Bruijn graph (Euler Tours)
all Lyndon words of length in orderDuval’s generation, amortised each
counting (aperiodic necklaces, Burnside)
runsevery run has a Lyndon root: fewer than runs (Runs)
bijective BWTdefined via the Lyndon factorisation

Two "Booth's algorithms"

Booth’s least-rotation algorithm (1980) is unrelated to Booth’s multiplication algorithm for signed binary multiplication.

vector<string> duval(const string& s) {                     // Lyndon factorisation, non-increasing factors
    int n = sz(s), i = 0; vector<string> res;
    while (i < n) {
        int j = i + 1, k = i;
        while (j < n && s[k] <= s[j]) { k = s[k] < s[j] ? i : k + 1; j++; }
        while (i <= k) { res.push_back(s.substr(i, j - k)); i += j - k; }
    }
    return res;
}
int leastRotationDuval(string s) {                          // start index of the smallest rotation (smallest index on ties)
    int n = sz(s); s += s; int i = 0, ans = 0;
    while (i < n) {
        ans = i; int j = i + 1, k = i;
        while (j < 2 * n && s[k] <= s[j]) { k = s[k] < s[j] ? i : k + 1; j++; }
        while (i <= k) i += j - k;
    }
    return ans;
}
int leastRotationTwoPointer(const string& s) {              // Booth-style two pointers, O(n)
    int n = sz(s), i = 0, j = 1, k = 0;
    while (i < n && j < n && k < n) {
        char a = s[(i + k) % n], b = s[(j + k) % n];
        if (a == b) { k++; continue; }
        if (a > b) i += k + 1; else j += k + 1;
        if (i == j) j++;
        k = 0;
    }
    return min(i, j);
}
bool isLyndon(const string& s) { auto f = duval(s); return sz(f) == 1; }
vector<vector<int>> lyndonWords(int n, int sigma) {         // all Lyndon words of length <= n, lexicographic
    vector<vector<int>> res; vector<int> w = {0};
    while (!w.empty()) {
        res.push_back(w); int m = sz(w);
        while (sz(w) < n) w.push_back(w[sz(w) - m]);        // repeat periodically
        while (!w.empty() && w.back() == sigma - 1) w.pop_back();
        if (!w.empty()) w.back()++;
    }
    return res;
}
vector<int> deBruijn(int sigma, int n) {                    // cyclic sequence of length sigma^n containing every word once (FKM)
    vector<int> seq;
    for (auto& w : lyndonWords(n, sigma)) if (n % sz(w) == 0) seq.insert(seq.end(), all(w));
    return seq;
}