Purpose: Karp-Miller-Rosenberg (1972) — the prefix doubling technique. Assign a canonical integer name to every substring of length , for every , in total. This single idea underlies suffix array construction, string comparison in , and a whole family of string DP tricks.

Algorithm

Let be the class label of the substring s[i .. i+2^k).

  1. = the character s[i] (or its rank in the alphabet).
  2. For each : form the pair

    for every , sort the pairs (radix sort in ), and let be the rank of ‘s pair. Two positions get the same label iff their length- substrings are identical.
  3. Stop after rounds.

Code

// id[k][i] = class of s[i .. i+2^k), for all k. O(n log n) time and memory.
vector<vector<int>> kmr(const string& s) {
    int n = s.size(), LOG = 1;
    while ((1 << LOG) < n) LOG++;
    vector<vector<int>> id(LOG + 1, vector<int>(n));
 
    {   // level 0
        vector<int> ord(n); iota(ord.begin(), ord.end(), 0);
        sort(ord.begin(), ord.end(), [&](int a, int b){ return s[a] < s[b]; });
        int c = 0;
        for (int i = 0; i < n; i++) {
            if (i && s[ord[i]] != s[ord[i-1]]) c++;
            id[0][ord[i]] = c;
        }
    }
 
    for (int k = 0; k < LOG; k++) {
        int len = 1 << k;
        vector<pair<pair<int,int>,int>> a(n);
        for (int i = 0; i < n; i++)
            a[i] = {{id[k][i], i + len < n ? id[k][i + len] : -1}, i};
        sort(a.begin(), a.end());
        int c = 0;
        for (int i = 0; i < n; i++) {
            if (i && a[i].first != a[i-1].first) c++;
            id[k+1][a[i].second] = c;
        }
    }
    return id;
}

Complexity

  • Time: with radix sort per level, with std::sort
  • Space: if you keep every level; if you only need the last

What the table buys you

Once id[][] exists, compare any two substrings of equal length in : split the length into two overlapping powers of two,

with . This is a deterministic alternative to string hashing — no collision risk, no anti-hash tests, at the cost of memory.

Variants / Use Cases

  • Suffix array construction — prefix doubling is KMR; the suffix array is just the final ordering
  • substring comparison — for suffix ordering, lexicographic DP, and canonical forms without hashing
  • Longest common extension (LCE) — binary search on length using equality tests, giving LCE
  • Tandem repeats and periodicity queries
  • SA-IS — the alternative when memory is too much
  • Hashing — the randomised competitor: memory, compare, small failure probability