Exercises — Naive pattern matching — O(nm)
Before we begin, every symbol used on this page, defined once in plain words:
Level 1 — Recognition
Exercise 1.1
For "XYZXYZ" and "XYZ", how many candidate shifts exist, and what are they? (Not asking which match — just how many positions we are allowed to try.)
Recall Solution
WHAT: count candidate starts. WHY: may only start where all of its characters still fit inside — beyond that it runs off the end.
Here , . The formula from the parent note is .
The candidate shifts are .
WHAT IT LOOKS LIKE: look at figure below — the ruler XYZ can sit at 4 different starting positions before its right edge would poke past the end of XYZXYZ.
Exercise 1.2
In the trace, we say a shift "fails at ". What does the number mean here, in plain words?
Recall Solution
is the inner-loop counter — it walks across the pattern one letter at a time, . "Fails at " means: matched , matched , but did not match . So two characters matched and the third broke the streak. We then stop (early break) and slide to .
Level 2 — Application
Exercise 2.1
Fully trace naive matching for "AABAACAADAABAAABAA" and "AABA". Report the count of comparisons at the first shift and list all valid shifts. Use the Substring Search Problem convention (0-indexed).
Recall Solution
, , so shifts . Write out with indices:
At : compare AABA against AABA. A=A, A=A, B=B, A=A → 4 comparisons, MATCH. So is valid.
Walking the rest (mismatches stop early):
- :
ABAAvsAABA→ A=A, B≠A, fail at . - :
AABA→ MATCH. - :
AAAB→ A=A, A=A, A≠B, fail at . - :
AABA→ MATCH. - All other shifts fail on the first or second character. Valid shifts: . Comparisons at : (the full pattern, because it matched).
Exercise 2.2
For the same as 2.1, how many candidate shifts are there, and what is the theoretical upper bound on total comparisons?
Recall Solution
Candidates: . Upper bound on comparisons: . This is the ceiling — the early break means the real count is usually far below it.
Level 3 — Analysis
Exercise 3.1
For "AAAAA" () and "AAB" (), count the exact number of character comparisons the algorithm performs (with early break). Then explain in one sentence why the break never helps here early.
Recall Solution
WHAT: hand-count every comparison. WHY: this exposes why "all-same-text + trailing mismatch" is the killer input. Candidate shifts: , so .
- :
AABvsAAA→ A=A (1), A=A (2), B≠A (3). Fails at after 3 comparisons. - :
AABvsAAA→ 3 comparisons, fail. - :
AABvsAAA→ 3 comparisons, fail. Total = comparisons. Note — the bound is hit exactly. Why break never saves us early: every character of the text isA, so the first letters of always match; only the finalBmismatches. The break fires on the last comparison, so we still pay full each shift.
Exercise 3.2
Give the exact comparison count for the parent's worst case "AAAAAAAAA" (), "AAAB" ().
Recall Solution
Shifts: . Each shift matches AAA then fails on the last B, costing the full comparisons.
Total = comparisons — matching the parent note's figure. This is made concrete.
Level 4 — Synthesis
Exercise 4.1
Design the best-case input for naive matching: choose and (both length-controllable) so the total comparison count is as small as possible, and give that count as a formula in and .
Recall Solution
WHAT: minimise total comparisons by choosing inputs. WHY: the outer loop always runs times (fixed); the only lever we control is the inner cost per shift.
To make each shift cheap, force a mismatch on the very first comparison at every shift. Achieve this by making a character that never appears in .
Example: "AAAAAAA", "BAA". At every shift, A B → fails immediately after 1 comparison.
Formula: total comparisons, i.e. .
WHAT IT LOOKS LIKE: the ruler slides all the way across but only ever peeks at its first letter each time — never spelling further.
Exercise 4.2
Modify the pseudocode so it stops after finding the first match instead of all matches. Give the new worst-case comparison count for a pattern that appears only at the very end of .
Recall Solution
Add a return s (or break on the outer loop) right after report match at s:
for s = 0 to n - m:
j = 0
while j < m and T[s+j] == P[j]:
j = j + 1
if j == m:
return s # stop at first matchWorst case for "match only at the end": the match sits at the last legal shift . We must try all earlier shifts first. If each earlier shift costs up to comparisons and the final matching shift costs : total still. Stopping early helps only when the match is early; if the sole match is at the tail, we do essentially the full worst-case work anyway.
Level 5 — Mastery
Exercise 5.1
An adversary must build the worst possible input over the alphabet A,B with fixed and . Construct and that force the maximum total comparisons, state that maximum, and prove no other choice beats it.
Recall Solution
WHAT: maximise total comparisons under fixed . WHY: the outer count is fixed; the adversary's only weapon is making every shift cost the full .
To cost at a shift, the first characters must match and the last must mismatch (a full-length near-miss).
Choose "AAB" and "AAAAAAAAAAAA" (twelve As). At every shift AAB vs AAA: A=A, A=A, B≠A → 3 comparisons, and it never actually matches, so the outer loop never short-circuits.
Maximum total comparisons.
Proof it's optimal: no shift can ever cost more than comparisons (the inner loop is bounded by ), and there are only shifts. Hence total for any input. Our construction attains , so it is a maximiser. ∎
Exercise 5.2
Two inputs both have . Input X = ("ABABABABAB", "AB"). Input Y = ("AAAAAAAAAA", "AB"). Which does more total comparisons, and why does the "many matches" input NOT automatically mean more work?
Recall Solution
Shifts for both: , so .
Input X (ABABABABAB): at even , AB=AB → 2 comparisons + MATCH. At odd , BA → fail at , 1 comparison.
Even shifts : five of them, each 2 comparisons = .
Odd shifts : four of them, each 1 comparison = .
X total comparisons (with 5 matches).
Input Y (AAAAAAAAAA, AB): every shift → A=A (1), B≠A (2) → 2 comparisons, 0 matches.
Y total comparisons (with 0 matches).
Conclusion: Y does more work () despite finding zero matches, while X finds five matches with less work.
Why "many matches" ≠ "more work": cost is driven by how deep each comparison chain goes before stopping, not by whether it ends in a match. In X, odd shifts die instantly (); in Y, every shift is forced to the last character. Depth of near-misses, not number of successes, sets the bill.
Active Recall
Recall lines:
Largest possible total comparisons for fixed n and m
A, pattern AA...AB).Cheapest run: what makes each shift cost only 1 comparison
Does "return at first match" change worst-case Big-O
Why can a zero-match input cost more than a many-match input
Connections
- KMP Algorithm — kills the L3/L5 worst case by reusing matched info.
- Boyer-Moore Algorithm — skips ahead so most shifts cost far less than .
- Rabin-Karp Algorithm — hashing makes each window expected.
- Big-O Notation — the bound language used throughout.
- Substring Search Problem — the parent problem these exercises probe.