KMP guarantees linear time by never backing up the text pointer. Boyer-Moore takes the opposite bet: compare the pattern from the right, and when a character mismatches, skip ahead as far as that character allows. When the alphabet is large and the pattern is short, most windows fail on their last character — so the pattern jumps many positions per comparison and often runs faster than linear.
How It Works
- Precompute the last-occurrence table — for every character, the rightmost index where it appears in the pattern. Characters that don’t appear get
-1. - Align the pattern under the text and compare characters from right to left.
- On a mismatch at pattern position
jagainst text characterc, shift the window right by:
Ifshift = j - last[c]cappears in the pattern at positionlast[c], this aligns that occurrence with the mismatch. Ifcis absent (last[c] = -1), the whole window slides past the mismatch. - On a full match, record the position and shift by 1 to keep scanning.
Key Insight
The bad-character rule exploits negative information: seeing a text character that isn’t in the pattern at all lets the window skip the entire pattern length in one step. And because scanning starts at the right edge, a mismatch almost always happens after just one comparison — so on typical text the number of text characters actually examined is far below n.
Worked Example
The visualizer searches for P = EXAMPLE in the classic text T = THIS IS A SIMPLE EXAMPLE.
The last-occurrence table is built from the pattern:
| char | E | X | A | M | P | L |
|---|---|---|---|---|---|---|
| last | 6 | 1 | 2 | 3 | 4 | 5 |
Alignment 1: EXAMPLE placed under THIS IS — the first right-edge comparison fails (S vs E). Since S never appears in the pattern, the window jumps 7 positions to just after the S. Later, alignment at SIMPLE fails at E vs M; M appears at pattern index 3, so the shift is 6 − 3 = 3, which lands EXAMPLE exactly on EXAMPLE — a full match at position 17.
Edge Cases & Pitfalls
- Shift ≤ 0: if the mismatching character’s last occurrence is to the right of
j,j - last[c]can be ≤ 0. Guard withmax(1, shift). - Pattern longer than text: trivially no matches; the main loop guard
i <= n - mhandles it. - Worst case: repetitive patterns on small alphabets (e.g. pattern of all the same character in a run) degrade to
O(nm)without the good-suffix rule. - Good-suffix rule: the full Boyer-Moore adds a second shift table for matched suffixes; the bad-character rule alone is the “Boyer-Moore-Horspool” simplification.
- Case sensitivity: the last-occurrence table is built over exact characters.
Comparison: Boyer-Moore vs Other Matchers
| Aspect | Naive | KMP | Boyer-Moore |
|---|---|---|---|
| Scan direction | Left-right | Left-right | Right-left |
| Average case | O(n + m) | O(n) | sublinear |
| Extra space | O(1) | O(m) | O(m) |
| Best for | Tiny patterns | Guaranteed linear | Large alphabets |
Applications
- Text search tools —
grep-style search over large files - Editors — fast “find” with big alphabets and short patterns
- Intrusion detection — scanning network payloads for signatures
Practice Trajectory
- Build the last-occurrence table for
EXAMPLEand confirm the values above. - Trace the visualizer’s first jump past the
SinTHIS IS. - Explain why
max(1, shift)is required for correctness. - Compare the number of character comparisons with naive scanning.
- Research the good-suffix rule and why it restores the linear worst case.