Purpose: Find the rotation of a string that is lexicographically smallest (the least rotation), in time and space.

Two different "Booth's algorithms"

This page is about Booth’s least-rotation algorithm (1980). There is also Booth’s multiplication algorithm for signed binary multiplication in hardware — unrelated, and essentially never needed in competitive programming.

Algorithm

Booth’s original formulation runs a KMP failure function over the doubled string and tracks the index of the best rotation found so far.

The version everybody actually writes is the “smallest rotation” two-pointer (sometimes credited to Zhou / Shiloach), which is simpler and equally :

  1. Keep two candidate start positions , , and an offset .
  2. Compare s[(i+k) % n] with s[(j+k) % n].
    • Equal → increment and keep comparing.
    • s[i+k] > s[j+k] → every start in is beaten; jump .
    • s[i+k] < s[j+k] → symmetrically, jump .
    • After a jump reset , and if push forward by one.
  3. Stop when or when the larger pointer reaches . The answer is .

Code

int leastRotation(const string& s) {
    int n = s.size(), 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);
}
 
string smallestRotation(const string& s) {
    int p = leastRotation(s);
    return s.substr(p) + s.substr(0, p);
}

Paradigm

Elimination / amortized two pointers. Every comparison either advances or eliminates candidate starting positions permanently.

Complexity

  • Time: — the sum of all -advances and pointer jumps is bounded by
  • Space: for the index, if you materialise the rotated string

Why It Works

Suppose the first characters of the rotations starting at and agree, and then s[i+k] > s[j+k]. Take any start . Its rotation begins with the suffix s[i'..i+k] of the matched block, and the rotation starting at begins with the identical block followed by the strictly smaller character. So is beaten by and can be discarded. Since we only ever discard provably-dominated candidates, the surviving pointer is a true minimiser. ∎

Variants / Use Cases

  • Duval’s algorithm — the Lyndon-factorisation route to the same answer; equally and often preferred because the code is shorter
  • Canonical form of a cyclic string — compare necklaces, deduplicate cyclic sequences, hash cycles
  • Largest rotation — flip the comparison
  • Cyclic string matching — search a pattern in a circular text via
  • Graph / polygon canonicalisation — canonicalise a cycle’s vertex labels or a polygon’s vertex order before hashing