Learn
Course · Algorithms

Strings and Pattern Matching

This is the course where the asymptotically better algorithm loses, three times, on inputs one click apart. Naive matching beats Knuth–Morris–Pratt on prose with an absent pattern; Horspool beats both by a factor of three on English and loses to naive matching by a factor of five on a run of one letter; a rolling hash reports three positions that are not matches until the characters are compared. None of the bounds moves while any of that happens, and every page prints both columns.

m(n − m + 1) naive and Horspool: one worst case, opposite inputs 2n KMP never backtracks in the text exactly n the automaton: one lookup per character, always a hash equal is a CANDIDATE; verifying it is the correctness argument output links chain SUFFIXES: he is reported with she, abc never with abcd distinct substrings = n(n + 1)/2 − sum of the LCP array
What this course is about
Subject
Algorithms
Lessons
12
Format
Interactive

Overview

What you will be able to do

By the end you can state a matcher's proved bound and its measured cost on one input without confusing them, construct the worst case for any of the four matchers from its own rules, and say which of the two structures at the end answers a question the matchers cannot.

1

Separate a count from a bound on every page

Naive matching makes 50 comparisons where its bound allows 123, and 100 where its bound allows 100. Both numbers are correct and only one of them is about all texts — and on a third input the count beats the algorithm with the better bound.

2

Build a worst case from an algorithm's own rules

Read the shift rule and the comparison order as separate requirements and defeat each: the run of a's that makes Horspool five times slower than naive matching, and the reversed pattern on the same text that swaps the two numbers exactly.

3

Construct the preprocessing rather than quote it

Borders found by trying every length beside the linear construction, the amortised total counted rather than asserted, and the transition table written out in full so that the failure function is recognisable as its compressed form.

4

Say what a hash establishes, and what it does not

Three windows on the lab's prose share the pattern's residue and are not the pattern. The verification is not an optimisation to skip, the collision count is a property of the text and the modulus together, and the bound behind it is an expectation over a random modulus rather than a promise about the one chosen.

Lessons

12 lessons

The simplest matcher first, at its best, at its reversal and at its worst case, because every later algorithm is an answer to what it throws away; then three ways of keeping that information, from the failure function to a full transition table; then skipping, with its saving and its collapse; then hashing, where a new kind of wrong answer appears; and last the two structures that preprocess the patterns together or the text itself.

Naive Matching, Measured and BoundedSlide the pattern over every offset, count the characters actually compared, and put that count beside the worst case it cannot exceed.Not marked
Where the Simple Algorithm WinsOn a pattern that is not in the text, naive matching makes 39 comparisons and Knuth–Morris–Pratt makes 43, and the reason is structural rather than accidental.Not marked
The Text Built to Reach the BoundConstruct the input on which the measured count equals m(n − m + 1) exactly, and then show what it takes to make naive matching actually quadratic.Not marked
Borders, and the Failure Function BuiltDefine the longest proper border of every prefix, compute it twice — once by trying every length and once in linear time — and check the two columns against each other.Not marked
The Amortised Bound, as a CountThe inner loop of the failure-function construction can run many times for one character; count the total and put it beside 2m.Not marked
The Matcher as a MachineWrite out every transition in advance and the search becomes exactly one table lookup per character, with no comparison and no back-up at all.Not marked
Shifting by More Than OneCompare the window from the right, then jump by the last occurrence of the character under its final position, and read 19 of 66 characters.Not marked
The Text Where Nothing Is SkippedThirty a's searched for baaaa: every shift is 1, nothing is skipped, and the skipping algorithm makes five times the comparisons the naive one does.Not marked
A Rolling Hash, and a Collision You Can ReadReduce each window to one number computed from the last, then read off the three windows whose number matches the pattern's and whose characters do not.Not marked
Choosing a Modulus, and What the Sweep SaysZero collisions at one modulus, and 18 of the 46 primes under 200 collide on the very same text — including one larger than several that do not.Not marked
One Pass for Every WordStore a dictionary as a trie, add a failure link to every node, and find every occurrence of every word in one scan of the text.Not marked
The Suffix Array, and What a Sorted Order AnswersSort every suffix of the text, record how much each shares with the one above it, and read the longest repeat and the exact number of distinct substrings off the result.Not marked

Recommended background

Discrete Mathematics in full, and by name: Strings and String Operations, Big-O, Big-Omega and Big-Theta, Analysing Iterative Algorithms, Loop Invariants and Program Correctness, Amortised Analysis, Strong Induction, Modular Arithmetic and Modular Exponentiation, and Trees. From this path: Data Structures and Their Cost Models, for the map and array trade the trie mode prints as two numbers, and Sorting and Selection, for the comparison counting every page here does. Geometric Algorithms and Randomised Algorithms assume nothing from this course and may be taken before it.

Practice suggestions

Read the two columns before reading the prose. Every lab on this course prints a measured count and a proved bound, and on four of the twelve opening inputs the algorithm with the better bound makes more comparisons. The counts move when you type and the bounds do not, and that is the difference the course is about.

Change the preset before writing a conclusion down. Three pages share one lab and differ only in which text it opens on: “Naive Matching, Measured and Bounded”, “Where the Simple Algorithm Wins” and “The Text Built to Reach the Bound” have the same algorithm at 1.22, 1.00 and 5.00 comparisons per alignment. Any sentence that survives only one of them is a sentence about a text.

Distrust a matcher that merely returned an answer. Six routines on these pages report match positions and every one of them is checked against comparing the whole substring at every offset, on your own text, on every keystroke — because an algorithm whose idea is skipping can skip over a match, and a shorter list of correct positions is the one wrong answer that looks right.

What it does not cover

Boyer–Moore in full. The good-suffix rule needs a second table and a separate correctness argument, and the linear-time variants of it need a third; Horspool's single table is developed and counted here, and the sublinear bounds are stated nowhere.

Suffix trees, suffix automata and the linear-time suffix array constructions. The array and its LCP array answer every question this course asks, and prefix doubling reaches them in log2 n rounds; building a suffix array in O(n) is a real improvement that changes no claim made here.

Approximate matching. Edit distance is a dynamic program and is built on the preceding course; the string-specific machinery for approximate search — bit parallelism, the four-Russians speedup, filtering by exact seeds — is a separate subject and none of it appears.

Compression, and the Burrows–Wheeler transform. The transform is the last column of the sorted rotations and is one step from the suffix array, which makes it tempting; it belongs with compression rather than with matching, and no page here needs it.