= last column of the sorted rotations of ( a unique smallest sentinel), computable from the suffix array: . It clusters equal characters (good for RLE + move-to-front: bzip2), is invertible by the LF-mapping, and with rank queries becomes the FM-index: count occurrences of by backward search in (DNA aligners). Uses sais from Suffix Array. CSES 1113 String Transform is inverseBwt.

UseHowCost
transformsuffix array of with SA-IS
inverseLF mapping: stable sort of positions by character or counting
count occurrences of (FM-index)backward search with and with an table (wavelet tree: , bits)
locate occurrencesSA entries of the final interval (sampled SA in practice)
compressibilitynumber of BWT runs ; LZ77 factors
compression pipelineBWT move-to-front RLE / entropy coding
string bwt(const string& s) {                               // s must not contain '#'; result has exactly one '#'
    vector<int> v(sz(s) + 1, 0); for (int i = 0; i < sz(s); i++) v[i] = (unsigned char)s[i] + 1;
    vector<int> sa = sais(v, 257); int n = sz(v);
    string t = s + '#', res(n, ' ');
    for (int i = 0; i < n; i++) res[i] = t[(sa[i] + n - 1) % n];
    return res;
}
string inverseBwt(const string& b) {                        // LF mapping
    int n = sz(b); vector<int> nxt(n); iota(all(nxt), 0);
    auto key = [](char c) { return c == '#' ? -1 : (int)(unsigned char)c; };
    stable_sort(all(nxt), [&](int x, int y) { return key(b[x]) < key(b[y]); });   // row r's first char is b[nxt[r]]
    string res; int i = nxt[0];
    for (int k = 0; k + 1 < n; k++) { i = nxt[i]; res += b[i]; }
    return res;
}
struct FMIndex {                                            // lowercase text
    string b; vector<int> sa, C; vector<array<int, 27>> occ;   // occ[i][c] = #c in b[0, i), c = 0 for '#'
    FMIndex(const string& s) {
        vector<int> v(sz(s) + 1, 0); for (int i = 0; i < sz(s); i++) v[i] = s[i] - 'a' + 1;
        sa = sais(v, 27); int n = sz(v); b.assign(n, ' ');
        for (int i = 0; i < n; i++) { int j = (sa[i] + n - 1) % n; b[i] = j == n - 1 ? '#' : s[j]; }
        occ.assign(n + 1, {}); for (int i = 0; i < n; i++) { occ[i + 1] = occ[i]; occ[i + 1][b[i] == '#' ? 0 : b[i] - 'a' + 1]++; }
        C.assign(28, 0); for (int c = 0; c < 27; c++) C[c + 1] = C[c] + occ[n][c];
    }
    pair<int, int> range(const string& p) const {           // SA rows [lo, hi) whose suffixes start with p
        int lo = 0, hi = sz(b);
        for (int i = sz(p) - 1; i >= 0 && lo < hi; i--) { int c = p[i] - 'a' + 1; lo = C[c] + occ[lo][c]; hi = C[c] + occ[hi][c]; }
        return {lo, hi};
    }
    int count(const string& p) const { auto [lo, hi] = range(p); return max(0, hi - lo); }
    vector<int> locate(const string& p) const { auto [lo, hi] = range(p); vector<int> r; for (int i = lo; i < hi; i++) r.push_back(sa[i]); sort(all(r)); return r; }
};
vector<int> moveToFront(const string& s) { vector<int> order(256); iota(all(order), 0); vector<int> out; for (unsigned char c : s) { int i = find(all(order), (int)c) - order.begin(); out.push_back(i); order.erase(order.begin() + i); order.insert(order.begin(), c); } return out; }
string inverseMoveToFront(const vector<int>& v) { vector<int> order(256); iota(all(order), 0); string s; for (int i : v) { int c = order[i]; s += char(c); order.erase(order.begin() + i); order.insert(order.begin(), c); } return s; }