C++ problem-solving patterns.

Recognition · invariants · implementation · testing

A pattern is a compact description of the state that removes repeated work. “Contiguous and longest” suggests a window because the useful state has two moving boundaries. “Minimum steps with unweighted edges” suggests BFS because the queue processes vertices in nondecreasing distance. The chapter links below explain that connection, establish the invariant, and then implement it in C++.

First read Find the repeated work

Write the direct solution, name what it rescans, and identify the state that can preserve the answer.

While coding State the invariant

Say what is true before and after every iteration. Pointer movement and data-structure updates follow from it.

Before finishing Attack the edges

Empty, singleton, duplicate, impossible, disconnected, overflow, and maximum-size cases expose most defects.

Chapters

Recognition map

Use the statement and constraints to narrow the candidates. The signal is not a proof. It tells you which invariant to test against the examples and constraints.

Statement or constraintState to tryRepresentative problems
Sorted sequence with a pair, triplet, or boundary queryTwo pointersTwo Sum II, 3Sum, palindrome, container area
Longest or shortest contiguous regionSliding windowLongest unique substring, minimum window, replacement
Count subarrays with a target aggregatePrefix state + hash countsSubarray Sum Equals K, Contiguous Array, range sums
Values belong at indices 0…n−1 or 1…nCyclic placementFirst missing positive, disappeared numbers, duplicates
Pairs of start/end coordinatesSort and sweepMerge/insert intervals, rooms, overlap removal
A “next” function may revisit a stateFast and slow pointersLinked-list cycle, duplicate number, happy number
Path, component, subtree aggregate, exhaustive reachabilityDFSIslands, tree diameter, path sum, clone graph
Minimum edges or steps when every edge has equal costBFSWord ladder, rotting oranges, level order
Prerequisites or “must happen before”Topological orderCourse schedule, build order, alien alphabet
Groups merge and connectivity is queried repeatedlyDisjoint setRedundant edge, accounts merge, Kruskal
Nonnegative weighted shortest pathDijkstraNetwork delay, cheapest route, path effort variants
Negative edges or all-pairs distancesBellman–Ford or Floyd–WarshallNegative cycles, city thresholds, transitive costs
First valid index or monotone feasibilityBinary searchLower bound, rotated search, shipping capacity, Koko
Largest/smallest k without fully sortingBounded heapTop K frequent, closest points, merge k lists
Nearest greater/smaller boundaryMonotonic stackTemperatures, histogram, next greater
Window extremum while endpoints advanceMonotonic dequeSliding maximum, constrained DP transitions
Prefix lookup over many stringsTrieAutocomplete, word board, prefix replacement
Generate all valid configurationsBacktrackingSubsets, permutations, N-Queens, word search
Count ways, minimum cost, or maximum value with overlapDynamic programmingRobber, coin change, partition, edit distance
Dynamic prefix sums or ranksFenwick treeInversions, count smaller, order statistics
Dynamic arbitrary range aggregateSegment treeRange max/sum, bounded-value LIS transitions
Many static range min/max/gcd queriesSparse tableRange minimum and lowest common ancestor reductions
Borders, periods, or exact substring matchesKMP prefix functionRepeated substring, prefix occurrence, pattern search
Prefix match length at every positionZ functionPattern occurrences, string overlap, prefix comparisons
Static substring equality or length feasibilityDouble rolling hashRepeated substring, longest common substring
All palindrome radiiManacherLongest/count palindromic substrings
Maximize count/value after sorting by a keyGreedy + exchange argumentInterval scheduling, deadlines, fractional choices
Two players alternate over independent componentsNim or Grundy valuesPile games, subtraction games, split components

From direct solution to maintained state

  1. Read constraints first. A quadratic loop can be correct for n ≤ 2,000 and impossible for n = 200,000.
  2. Write the direct operation count. “For each right endpoint, I scan every earlier value” is more useful than only writing O(n²).
  3. Name the repeated query. Examples: “Have I seen this complement?”, “What is the largest live value?”, or “Which prefixes differ by k?”
  4. Choose state supporting that query. Hash table, deque, heap, bitset, tree, or DP table.
  5. State the invariant before implementation. Every movement or update must restore it.
  6. Count how often each item changes state. This establishes the actual complexity of windows, stacks, deques, and amortized structures.
  7. Test the direct and optimized versions together. Randomized differential tests are especially effective for prefix counts, trees, and range structures.

C++ mechanics that affect otherwise-correct algorithms

Promote before arithmetic

Casting after a signed overflow does not repair it. At least one operand must be widened before the operation.

wide_arithmetic.cpplong long product = 1LL * left * right;
long long distance = static_cast<long long>(a) + b;
int middle = low + (high - low) / 2;

Use half-open ranges consistently

The chapters use [begin, end). Length is end - begin, an empty range has equal endpoints, and adjacent ranges compose without adding or subtracting one.

half_open.cpplong long range_sum(
    const std::vector<long long>& prefix,
    std::size_t begin,
    std::size_t end
) {
    return prefix[end] - prefix[begin];
}

Fast input without mixed buffering

For input-heavy programs, detach C++ streams from the C stdio buffers and stop flushing cout before each read. After doing this, keep all input on cin. Do not mix it with scanf.

io_setup.cpp#include <iostream>

int main() {
    std::ios_base::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int count;
    std::cin >> count;
    // Use '\n' for ordinary line endings; std::endl also flushes.
}
Tested companion

The repository package compiles with C++20, strict conversion and shadowing warnings, and no test framework. It runs fixed edge cases plus 4,000 deterministic comparisons against direct implementations. See examples/cpp-patterns.