Intuition The ONE core idea
Boyer-Moore searches for a small pattern inside a big text by lining the pattern up under the text and comparing characters from the right end backwards . The magic is that a single mismatch near the right tells you so much about where the pattern cannot fit that you can leap forward many positions at once — often without even reading most of the text.
Before you can understand the two clever "skip" rules (bad character and good suffix) in the parent note , you need a rock-solid grip on a handful of symbols and pictures. This page builds every single one from nothing. Read it top to bottom; each item leans on the one above it.
Definition String, character, index
A string is just a row of boxes, and each box holds one character (a letter, digit, or symbol). The index is the label written under each box, and — this is the rule that trips up beginners — counting starts at 0 , not 1.
Look at the figure. The word ABCAB is drawn as five boxes. Under each box is its index: 0, 1, 2, 3, 4. So:
The first box is at index 0 .
The last box is at index 4 .
Intuition Why index-from-zero matters here
Boyer-Moore constantly talks about "the character at index j " and "the last character". If a string has length m (the number of boxes), then the last box is at index m − 1 , not m . Boyer-Moore literally starts every comparison at m − 1 , so you must feel this in your bones.
Definition Text and pattern
T = the text : the long string we are searching inside . Its length is called n .
P = the pattern : the short string we are searching for . Its length is called m .
Reading a single box: T [ k ] means "the character in box number k of the text", and P [ i ] means "the character in box number i of the pattern". The square brackets are just how we point at one box.
Intuition Picture them as two strips of paper
Imagine T is a long strip taped to your desk. P is a short strip you slide along on top of it. Matching = finding every place where the short strip's letters agree with the long strip's letters underneath.
Because P is the short one, m ≤ n whenever a match is even possible. And the pattern can only start at positions 0 through n − m — past that, the short strip would hang off the right edge of the desk.
Definition Alignment / shift
s
The shift s is the index in the text where the pattern's left edge currently sits. So when the pattern is at shift s , box P [ i ] lies directly above box T [ s + i ] .
The figure shows the pattern strip CCTTTTGC placed with its left edge at s = 2 on the text strip. Trace one vertical line: P [ 0 ] sits over T [ 2 ] , P [ 1 ] over T [ 3 ] , and in general:
P [ i ] is above T [ s + i ] .
Intuition Why this "+s" bookkeeping matters
Every comparison in Boyer-Moore is really "does the pattern box P [ i ] match the text box directly under it?" — that under-box is T [ s + i ] . When we later "shift the pattern right by k ", we simply do s ← s + k : the strip slides k boxes to the right.
Definition Right-to-left comparison
Boyer-Moore does not start comparing at the left (P [ 0 ] ). It starts at the rightmost box P [ m − 1 ] and walks leftward (m − 1 , m − 2 , m − 3 , … ) as long as boxes agree.
WHAT we do: put a finger on P [ m − 1 ] and on T [ s + m − 1 ] , check they match, then step both fingers one box left.
WHY we do it: if we get a mismatch after having matched, say, the last three boxes, we now know the text ends with those three pattern characters. That "known suffix" is exactly the fuel for the big-skip rules. Comparing left-to-right (like naive search) would only ever tell us about a matched prefix , which the skip rules cannot use.
WHAT IT LOOKS LIKE : in the figure the green ticks on the right show matched boxes; the red cross shows the first mismatch as we move left.
Definition Mismatch index
j , bad character c
As we walk leftward, the first box where pattern and text disagree is at pattern index j . So P [ j ] = T [ s + j ] . The offending text character sitting under that box, c = T [ s + j ] , is called the bad character .
Everything to the right of j already matched: P [ j + 1 … m − 1 ] agrees with the text. Everything at j and left of it is not yet checked (except j itself, which just failed).
In the figure: matched suffix is coloured, the box at index j is crossed out, and the text character under it is circled and labelled c . Two facts to carry forward:
The matched suffix P [ j + 1 … m − 1 ] has length m − 1 − j . (Count the boxes: from j + 1 up to m − 1 .)
The bad character c is a text character — it may or may not appear anywhere inside P .
j is a text index."
Why it feels right: the mismatch happens in the text too.
The fix: j is a pattern index. The matching text box is found by adding the shift: T [ s + j ] . Mixing these up is the single most common Boyer-Moore bug.
Intuition Picture: front-chunks and back-chunks
Take ABCAB. Its prefixes are A, AB, ABC, ABCA, ABCAB. Its suffixes are B, AB, CAB, BCAB, ABCAB. Notice AB shows up as both a prefix and a suffix — that special coincidence has its own name (next section), and it is the whole heart of the good-suffix rule.
A border of a string is a proper substring (shorter than the whole string) that is simultaneously a prefix and a suffix. "Proper" just means it is not the entire string itself.
Worked example Borders of a word
In ANPANMAN (length 8):
AN is a prefix (front two boxes) and a suffix (back two boxes) → AN is a border.
The empty string is always a trivial border.
Borders answer the question: "if I already matched this back-chunk, where else in the pattern does the same chunk appear so I can reuse the match?" That is precisely Case 2 of the good-suffix rule you'll meet in the parent note. If you want the general machinery behind borders, see Borders and Prefix Function and its cousins Knuth-Morris-Pratt (KMP) and Z-Algorithm .
Definition Last-occurrence function
For any character c , last ( c ) is the largest index in P where c appears. If c never appears in P , we set last ( c ) = − 1 .
last ( c ) = { max { i : P [ i ] = c } − 1 c occurs in P c does not occur in P
Intuition Why "-1" and not "0" for absent characters
− 1 is a deliberate "before the string starts" marker. When the bad-character rule computes a shift as j − last ( c ) , plugging in − 1 gives j − ( − 1 ) = j + 1 , which slides the whole pattern past the bad spot. If we'd used 0 we would under-shift and waste work. The choice of − 1 is what makes "the character isn't in the pattern at all" behave as the biggest, cleanest jump.
Worked example Reading last() off a word
P = ABCAB . Then:
last ( A ) = 3 (rightmost A is box 3),
last ( B ) = 4 ,
last ( C ) = 2 ,
last ( Z ) = − 1 (no Z).
max symbol
max ( a , b ) simply picks the larger of two numbers. max ( 1 , x ) therefore means "use x , but never let it drop below 1 " — this is called clamping .
Intuition WHY clamp to 1, and WHY take the max of two rules
Clamp to 1 : a shift of 0 or a negative shift would move the pattern left or leave it still — the search would loop forever. Forcing at least 1 guarantees forward progress.
Max of the two rules: the bad-character rule and the good-suffix rule each independently promise "you will not skip over a real match if you jump this far." When two promises are both safe, the bigger jump is also safe (and faster). So Boyer-Moore takes max ( shift b c , shift g s ) .
This "each rule is a safe lower bound, take the larger" idea is the logical glue of the whole algorithm — hold onto it when you read the parent's boxed final formula.
Σ
Σ (Greek capital "sigma") is the set of all possible characters, and ∣Σ∣ is how many there are. For plain English text ∣Σ∣ ≈ 26 ; for DNA it's 4 (A,C,G,T); for bytes it's 256 .
Intuition Why the alphabet size decides the speed
A big alphabet means the bad character c usually does not appear in the short pattern, so last ( c ) = − 1 and we jump a full m boxes — skipping m text characters without reading them . Reading fewer than n characters total is what "sublinear " means. On tiny alphabets like DNA the pattern shares many characters with the text, jumps shrink, and the advantage fades. This is why Boyer-Moore shines on large alphabets and why relatives like Rabin-Karp , Aho-Corasick , and the Galil Rule exist for other regimes.
Text T length n, Pattern P length m
Slice notation prefix and suffix
Alignment shift s and T of s plus i
Mismatch index j and bad char c
Border prefix equals suffix
Alphabet size and sublinear
Every foundation box on this page feeds one of the two skip rules, and both rules feed the final search loop — exactly the structure of the parent note.
Test yourself — each line is question ::: answer.
If a string has length m , what is the index of its last box? m − 1 (indices run 0 to m − 1 ).
When the pattern is at shift s , which text box sits under P [ i ] ? T [ s + i ] .
In which direction does Boyer-Moore compare characters, and why? Right to left, so a mismatch reveals a matched suffix that powers the big-skip rules.
What are j and c at a mismatch? j is the pattern index where the first (rightmost-first) mismatch occurs; c = T [ s + j ] is the bad text character under it.
What is a border of a string? A proper substring that is both a prefix and a suffix.
What is last ( c ) when c does not occur in P ? − 1 .
Why is the shift clamped with max ( 1 , ⋅ ) ? To guarantee forward progress — a zero or negative shift would loop forever.
Why does Boyer-Moore take max of the two rules' shifts? Each rule is an independently safe jump; the larger of two safe jumps is still safe and faster.
What does "sublinear" mean here and when does it happen? Reading fewer than n text characters; happens with large alphabets where the bad character often isn't in P , so jumps of size m are common.
🇮🇳 Prefer Hinglish? See 3.8.05 Boyer-Moore — bad character, good suffix heuristics (Hinglish) .