Compression is rarely the goal in competitive programming, but its ideas — run-length encoding, dictionary references, entropy coding — appear constantly as problem structure.
Run-length encoding (RLE)
Replace runs of identical characters with (character, count) pairs.
vector<pair<char,int>> rle(const string& s) {
vector<pair<char,int>> r;
for (size_t i = 0; i < s.size(); ) {
size_t j = i;
while (j < s.size() && s[j] == s[i]) j++;
r.push_back({s[i], (int)(j - i)});
i = j;
}
return r;
}Where it matters in problems:
- The input has characters but only runs — solve on the RLE, not the string.
- Operations that act on whole runs (flip a range, sort a range) — an interval map in a
std::mapis RLE with updates, and is the “Chtholly tree” technique. - Counting: “number of maximal blocks”, “longest run”, “minimum changes to make all runs length ”.
- Grid problems where rows repeat.
Lempel-Ziv
LZ77 replaces a substring by a reference to an earlier occurrence: a triple (distance, length, next character). LZ78 / LZW builds a dictionary of phrases instead.
The LZ factorisation (also called the s-factorisation) — the greedy decomposition of into the longest factors each of which occurred earlier — is a genuinely useful string-theory object:
- computable in from a suffix array + LCP, or from a suffix automaton;
- the number of factors is a measure of the string’s repetitiveness;
- it is the basis of the runs algorithm and of several repetition-detection results;
- where is the number of BWT runs, and both are for compressible strings.
Contest appearances are rare but real: “compress this string optimally with back-references” is an LZ factorisation, and greedy is provably optimal for the factor count.
Huffman coding
Optimal prefix-free coding by symbol frequency: repeatedly merge the two least frequent symbols. with a heap. See Huffman Coding.
The competitive-programming form is usually disguised: “merge piles, cost = sum of the two merged, minimise the total” — identical problem, and the answer is the Huffman tree’s weighted path length.
When the leaf order is fixed, greedy fails and you need Hu-Tucker or an Knuth-optimised interval DP.
Burrows-Wheeler transform
Sort all rotations of $s$$ and take the last column. The result clusters equal characters together, which makes it highly compressible by RLE + move-to-front.
- Computable in from the suffix array: .
- Invertible via the LF-mapping, in .
- The basis of
bzip2and, with a rank structure (wavelet tree), of the FM-index used by DNA aligners.
Entropy — the theoretical floor
Shannon’s source coding theorem: no lossless code can beat the entropy
Huffman is within 1 bit per symbol of ; arithmetic coding gets arbitrarily close.
Useful as a lower-bound argument: “any comparison sort needs comparisons” is the same counting argument, and information-theoretic bounds of this shape are a standard way to prove a problem cannot be solved faster.
The techniques worth carrying
- Work on the RLE, not the raw string, when runs are few.
- Interval maps (
std::mapof maximal constant intervals) for range-assign operations. - Counting arguments — entropy and give lower bounds.
- Back-reference structure — the LZ factorisation is the right formalism for “how repetitive is this string”.
See also: Huffman Coding · Suffix Array · Complexity Theory