Skip to content

fix: bad-character shift in Boyer-Moore search has no effect - #15010

Open
bolverk wants to merge 1 commit into
TheAlgorithms:masterfrom
bolverk:fix/boyer-moore-bad-character-shift
Open

fix: bad-character shift in Boyer-Moore search has no effect#15010
bolverk wants to merge 1 commit into
TheAlgorithms:masterfrom
bolverk:fix/boyer-moore-bad-character-shift

Conversation

@bolverk

@bolverk bolverk commented Aug 7, 2026

Copy link
Copy Markdown

Fix bad-character shift having no effect (dead loop variable)

Fixes #14844

Summary

bad_character_heuristic() in strings/boyer_moore_search.py assigned the
computed shift to the for-loop variable i:

for i in range(self.textLen - self.patLen + 1):
    ...
    i = (mismatch_index - match_index)  # no effect: next iteration overwrites i

In Python, reassigning a for-loop variable inside the loop body has no effect
on the iteration sequence, so the shift was dead code and the search silently ran
as a plain O(n·m) scan over every position.

The rewrite uses a while loop so the shift actually advances the search, and
computes the shift in the pattern's coordinate space (the mismatch offset and
the alignment target are both pattern indices), instead of mixing a text index
with a pattern index as the old code did.

Correctness

The new algorithm was first written in Dafny and machine-verified (15 proof
obligations, 0 errors machine-checked by the SMT-based verifier) to establish:

  • Soundness — every emitted position is a genuine occurrence of pattern.
  • Completeness — no occurrence is skipped by the shift.

The key safety invariant is documented in the docstring: a jump maps the
mismatching text position onto a pattern index that carries a character unequal
to it, so no skipped alignment can be a match.

In addition to the proof, the implementation agrees with a brute-force scan on
30,000 randomized inputs (empty text, single-character and repeated patterns).

Impact (the issue's own reproduction)

text = "ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP", pattern = "MNOP":

iterations matches
before (brute-force) 29 [12, 28]
after 9 [12, 28]

The existing doctest BoyerMooreSearch("ABAABA", "AB").bad_character_heuristic()
still returns [0, 3].

Checklist

  • Fix a bug or typo in an existing algorithm
  • I have read CONTRIBUTING.md
  • This pull request is all my own work
  • This PR only changes one algorithm file
  • Passes ruff check, ruff format --check, and the doctests

Reassigning the for-loop variable in bad_character_heuristic() does not
change Python iteration, so the bad-character shift was dead code and the
search ran as brute force. Convert to a while loop so the shift advances
the search, and compute the shift in the pattern's coordinate space.

The revised algorithm is machine-verified with Dafny (soundness and
completeness); it agrees with a brute-force scan on 30k random inputs.
@algorithms-keeper algorithms-keeper Bot added enhancement This PR modified some existing files awaiting reviews This PR is ready to be reviewed labels Aug 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

awaiting reviews This PR is ready to be reviewed enhancement This PR modified some existing files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: Boyer-Moore bad character shift has no effect (for-loop variable reassignment)

2 participants