Worked examples — Naive pattern matching — O(nm)
Two things we will count in every example:
- Shift — the starting index in the text where we lay the pattern down. Think of it as "how many steps right have we slid the transparent ruler?"
- Character comparisons — one comparison is checking a single letter against . This is the unit the cost is measured in.
The scenario matrix
Every input to a pattern matcher falls into one of these case classes. Our worked examples below are labelled with the cell they hit, and together they touch every row.
| # | Case class | What makes it special | Covered by |
|---|---|---|---|
| A | Match in the middle | ordinary case, break fires early on misses | Ex 1 |
| B | Match at the very start () | left edge, no sliding needed to find first hit | Ex 1 |
| C | Match at the very end () | right edge — pattern touches the last char | Ex 2 |
| D | No match at all | inner loop never reaches | Ex 3 |
| E | Overlapping matches | matches whose regions share characters | Ex 4 |
| F | Degenerate: | pattern is a single character | Ex 5 |
| G | Degenerate: | pattern as long as text — one shift only | Ex 6 |
| H | Empty pattern () | limiting/boundary input | Ex 6 |
| I | Worst case | early break never helps | Ex 7 |
| J | Best/expected case | mismatch on char 0 every time → | Ex 8 |
| K | Real-world word problem | DNA / text search framing | Ex 9 |
| L | Exam twist — count comparisons exactly | reason about cost, not just position | Ex 10 |
Example 1 — middle + start matches (cells A, B)
Here , , so shifts run .

- : compare "ABC" with "ABC" → A=A, B=B, C=C. All 3 match, reaches . MATCH at (cell B, left edge). Why this step? We start and walk the pattern; only when do we declare success.
- : "B" vs "A" → mismatch at . Break immediately. One comparison spent. Why this step? The early break kills the shift the instant a letter disagrees — no point checking "C" or "A" after.
- : "C" vs "A" → mismatch. Break.
- : "ABC" → match. MATCH at (cell A, middle).
- : "B" vs "A" → mismatch. Break.
- : "CAB" → C vs A mismatch. Break.
Answer: matches at .
Example 2 — match at the very end (cell C)
, , shifts .
- : the first letters "X","Y","Z","X","Y","Q" all differ from "P" except we must check each. At , "Q" vs "P" → mismatch. Every one of these breaks on the first char. Why this step? begins with "P", which appears in only near the end, so all early shifts die at .
- : "PQR" vs "PQR" → P=P, Q=Q, R=R. MATCH at . Why this step? is the last legal shift; the pattern's final char lands on , the last character of the text. Any larger would run off the end.
Answer: single match at .
Example 3 — no match anywhere (cell D)
, ? No — "WORLD" has length 5 and "HELLO" length 5, so . Shifts : only one candidate.
- : "H" vs "W" → mismatch at . Break. Why this step? With there's exactly one place to check, and the very first letter fails.
- Inner loop never reached , so no shift is reported.
Answer: no matches — empty result. Exactly 1 comparison was made.
Example 4 — overlapping matches (cell E)
, , shifts .

- : "AA" → match. MATCH at .
- : "AA" → match. MATCH at . Why this step? used characters at indices ; uses indices . Index 1 is shared — the two matches overlap. Naive matching happily reports both because it treats each shift independently.
- : "AA" → match. MATCH at .
Answer: matches at — three overlapping occurrences.
Example 5 — degenerate: single-character pattern (cell F)
, , shifts (that's shifts — one per character).
- Each shift compares exactly one character: vs "A". Why this step? When the inner loop runs at most once, so "does this shift match?" collapses to "is this letter an A?"
- Letters of "BANANA" by index: B(0), A(1), N(2), A(3), N(4), A(5). Matches where the letter is "A": .
Answer: matches at .
Recall Why
makes cost exactly Every shift does comparison, and there are shifts, so total comparisons . The bound becomes — the search degenerates into a linear scan. ✔
Example 6 — degenerate lengths: and empty pattern (cells G, H)
(a) : shifts → only one shift.
- : compare all 4 chars C=C, O=O, D=D, E=E → match. MATCH at . Why this step? When the pattern is as long as the text there's literally nowhere to slide; you check the single alignment and stop.
(b) (empty pattern): shifts → shifts.
- The inner loop condition
j < misj < 0, false immediately, so it runs zero comparisons andj==m(both ) is instantly true. Why this step? An empty string is a substring of every position, including one past the last character. So the empty pattern "matches" at all positions .
Answers: (a) match at . (b) matches at (five positions).
Example 7 — the worst case (cell I)
, , shifts → 4 shifts.

- Every shift: "A"=="A" ✓, "A"=="A" ✓, then "A" vs "B" ✗. Why this step? The first characters always match (all A's), so the break only fires on the last comparison. We pay the maximum comparisons per shift.
- Total comparisons. Why this step? This is exactly the product in the formula reached with no early exit — the algorithm's ceiling.
Answer: 12 comparisons; zero matches. This is where naive matching earns its reputation, motivating KMP Algorithm and Boyer-Moore Algorithm.
Example 8 — best / expected case (cell J)
, , shifts → 5 shifts.
- Every shift: vs "X" → mismatch immediately (no letter of "ABCDEF" is "X"). Break after 1 comparison. Why this step? When the first pattern character is rare/absent, the break fires at every time — the inner loop never gets going.
- Total comparisons.
Answer: 5 comparisons, no matches — this is the behaviour that makes naive matching perfectly fine on typical text. Contrast with Ex 7's 12: same-ish sizes, wildly different cost.
Example 9 — real-world word problem: DNA search (cell K)
, , shifts .
- : "GATTACA" → MATCH. 7 comparisons (full pattern). Why this step? A successful shift always spends the full comparisons — it must confirm every letter.
- : "A" vs "G" → mismatch, break after 1.
- : each first letter ("T","T","A","C","A") differs from "G" → break after 1 comparison each.
- : "GATTACA" → MATCH. 7 comparisons. Why this step? The second copy begins exactly at index 7; the two matches are non-overlapping because they abut ( covers , covers ).
Answer: motif found at and ; two non-overlapping occurrences. Total comparisons wait — count them: successes , plus failures at = 6 single comparisons → .
Example 10 — exam twist: count comparisons exactly (cell L)
, , shifts → 3 shifts.
- : "AB"="AB" → match. Comparisons: A=A ✓, B=B ✓ → 2 comparisons.
- : "B" vs "A" → mismatch. 1 comparison. Why this step? Break after the first letter; we never look at for this shift.
- : "AB"="AB" → match. 2 comparisons.
Answer: (a) matches at . (b) total comparisons. Note the upper bound ; we did 5, saving one thanks to the early break at .
Recap of the matrix
Recall Which example hit which cell?
Ex1 ::: A (middle) + B (start) Ex2 ::: C (right edge, ) Ex3 ::: D (no match) Ex4 ::: E (overlapping matches) Ex5 ::: F (single-char pattern, ) Ex6 ::: G () and H (empty pattern ) Ex7 ::: I (worst case ) Ex8 ::: J (best/expected ) Ex9 ::: K (real-world DNA search) Ex10 ::: L (exact comparison count, exam twist)
For text of length n and all-same-char pattern of length m inside all-same-char text, how many overlapping matches?
Where does an empty pattern (m=0) match in a text of length n?
In the worst case with pattern "AA...B", how many comparisons per shift?
Why does pattern "XY" against "ABCDEF" cost only ~n comparisons?
When m=n, how many candidate shifts are there?
Connections
- Parent: the algorithm these examples exercise
- KMP Algorithm — turns Ex7's into by reusing matched info.
- Boyer-Moore Algorithm — skips shifts like Ex8's failures even faster.
- Rabin-Karp Algorithm — compares each window in expected via hashing.
- Big-O Notation — the language for the cost counts above.
- Sliding Window Technique — the "slide the ruler" model behind every shift.
- Substring Search Problem — the umbrella problem all cases belong to.