Séance 2 — Boyer-Moore-Horspool
1. Les limites de l'algorithme naïf
L'algorithme naïf décale toujours d'un seul caractère à chaque échec. Boyer-Moore-Horspool améliore cela avec deux idées :
- On compare le motif au texte en partant de la fin du motif (droite vers gauche).
- En cas d'échec, on décale de plusieurs positions d'un coup grâce à une table de décalage.
2. Exercice 1 — Table de décalage : « banane »
Pour chaque caractère du motif (sauf le dernier), à l'indice i, le décalage associé est m - 1 - i. En cas de répétition, on garde le décalage de la dernière occurrence.
motif = "banane" (m = 6, indices 0 à 5 : b-a-n-a-n-e)
a) Indice et décalage pour chaque caractère (hors dernier). b) Cas du « a » répété. c) Cas du « n » répété. d) Décalage pour un caractère absent du motif ?
Voir la réponse
Table finale : {b: 5, a: 2, n: 1} — tout autre caractère (absent du motif, ex. « z ») : décalage m = 6 (on peut sauter toute la longueur du motif).
3. Exercice 2 — Table de décalage : « chercher »
Même méthode sur le motif « chercher » (attention aux lettres répétées c, h, e, r).
Voir la réponse
Table finale : {c: 3, h: 2, e: 1, r: 4} — autres caractères : décalage 8.
4. Exercice 3 — Dérouler l'algorithme à la main
Avec la table de l'exercice 2, déroule la recherche de « chercher » dans « chercher, rechercher et chercher encore ». Compte les comparaisons et compare au résultat naïf (54).
Voir la réponse
5. Exercice 4 — Implémentation Python
NO_CAR = 256
def recherche_boyer_moore(txt, motif):
m = len(motif)
n = len(txt)
tab_car = [m] * NO_CAR
for i in range(m - 1):
tab_car[ord(motif[i])] = m - 1 - i
decalage = 0
res = []
while decalage <= n - m:
j = m - 1
while j >= 0 and motif[j] == txt[decalage + j]:
j = j - 1
if j < 0:
res.append(decalage)
decalage += 1
else:
decalage += tab_car[ord(txt[decalage + j])]
return res
a) Vérifie que tab_car correspond à tes calculs manuels. b) Teste sur l'exemple de l'exercice 3. c) Ajoute un compteur de comparaisons.
Voir la réponse
nb_comparaisons = 0
while j >= 0 and motif[j] == txt[decalage + j]:
nb_comparaisons += 1
j = j - 1
if j >= 0:
nb_comparaisons += 1 # la comparaison qui a échoué compte aussi
6. Exercice 5 — Comparaison expérimentale
a) Sur un motif court et fréquent, lequel est le plus rapide ? b) Sur un motif long et rare ?
Voir la réponse
Exercices défis (bonus)
Trouve un texte/motif où Boyer-Moore-Horspool ne fait pas mieux que l'algorithme naïf.
Voir la réponse
texte = "a" * 1000
motif = "a" * 20
# tous les caractères identiques : la table ne permet quasiment aucun saut utile
Version où le décalage dépend aussi de la position j dans le motif, pas seulement du caractère fautif.
def table_bm(m):
d = [{} for _ in range(len(m))]
for j in range(len(m)):
for k in range(j):
d[j][m[k]] = k
return d
def decalage(d, j, c):
if c in d[j]:
return j - d[j][c]
else:
return j + 1