T3.3 Recherche textuelle⚓︎
1. Recherche naïve et efficacité⚓︎
Illustration de l'algorithme
Exercice 1: BNS 2024, sujet 21, exercice 1
Écrire une fonction recherche_motif
qui prend en paramètre une chaîne de caractères
motif
non vide et une chaîne de caractères texte
et qui renvoie la liste des positions de
motif
dans texte
. Si motif
n’apparaît pas, la fonction renvoie une liste vide.
Exemples:
>>> recherche_motif("ab", "")
[]
>>> recherche_motif("ab", "cdcdcdcd")
[]
>>> recherche_motif("ab", "abracadabra")
[0, 7]
>>> recherche_motif("ab", "abracadabraab")
[0, 7, 11]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
Exercice 2: 1 ecicrexe ,12 tejus ,4202 SNB
Modifier la fonction précédente pour quelle effectue la recherche «à l'envers», c'est-à-dire en partant de la fin de motif
.
Illustration de l'algorithme
Cette fonction permet de rechercher n'importe quelle chaîne de caractères dans ... n'importe quelle chaîne de caractères. On utilise la plupart du temps (avec souvent le raccourci CTRL+F
) cette recherche pour chercher un fragment de code (un nom de variable à remplacer par exemple) dans un programme ou bien pour chercher une occurence d'un mot dans une page web.
Pour mesurer l'efficacité de cet algorithme naïf, nous allons faire une recherche dans le texte intégral de L'Avare, de Molière, mis à disposition légalement par le Projet Gutenberg .
Exercice 3
Mesurer le temps d'exécution de la fonction recherche_motif
sur 3 chaînes de caractères:
- une courte, dont on est sûr.e qu'elle apparaît dans le texte;
- une (plus) longue, dont on est sûr.e qu'elle apparaît dans le texte (par exemple
'Il faut, pour me donner conseil, que je voie ma cassette.'
); - une dernière dont on est sûr.e qu'elle n'apparaît pas dans le texte (par exemple
'Iron Man'
,'Gouygou'
,"le gras c'est la vie."
, etc.).
On peut utiliser pour cela le code suivant (à compléter):
1 2 3 4 5 6 7 8 9 10 11 12 |
|
2. Algorithme de Boyer-Moore(-Horspool)⚓︎
2.1 Principe⚓︎
L'idée est d'améliorer le code précédent (celui où on parcourt le motif à l'envers) en sautant directement au prochain endroit potentiellement valide.
Pour cela on regarde le caractère X
du texte sur lequel on s'est arrêté (car X
n'était pas égal au caractère de rang équivalent dans le motif):
-
si
X
est dans le motif (sauf à la dernière place du motif !), on va regarder la place de la dernière occurence deX
dans le motif et de déplacer de ce nombre, afin de faire coïncider leX
du motif et leX
du texte. -
si
X
n'est pas dans le motif, il est inutile d'avancer "de 1" : on retomberait tout de suite surX
, c'est du temps perdu. On se décale donc juste assez pour dépasserX
, donc de la longueur du motif cherché.
Illustration de l'algorithme
2.2 Prétraitement⚓︎
Dans cet algorithme, on a donc besoin de savoir si un caractère appartient ou non au motif, et si oui, quel est le rang de sa dernière occurence dans le motif. On exclut le dernier caractère du motif cependant, car sinon cela poserait un problème lors du décalage (on décalerait de 0...).
Exercice 4
Écrire une fonction dico_lettres
qui prend en paramètre une chaîne de caractères mot
et qui renvoie un dictionnaire associant à chaque caractère de mot
(sauf le dernier) son dernier indice dans mot
.
Exemple:
>>> dico_lettres('MARGUERITE')
{'M': 0, 'A': 1, 'R': 6, 'G': 3, 'U': 4, 'E': 5, 'I': 7, 'T': 8}
1 2 3 4 5 |
|
2.3 Implémentation de l'algorithme de BMH⚓︎
Il s'agit maintenant de modifier la recherche naïve «à l'envers», en avançant non plus l'indice de recherche i
systématiquement de 1, mais du bon nombre de caractères en fonction de l'apparition ou non du caractère dans le motif.
Un exemple à la main pour commencer...
Appliquer l'algorithme de BMH à la main (dans un tableau) pour la recherche du motif 'ATGCGA'
dans le texte 'AACATATGXGATGCGAGGTCGTAGT'
.
Exercice 5
-
Compléter la fonction de recherche par l'algorithme de Boyer-Moore-Horspool.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
def recherche_BMH(motif:str, texte:str) -> list: """ Renvoie une liste, éventuellement vide, des positions de motif dans texte. """ dico = ... positions = [] i = 0 while i <= len(texte)-len(motif): j = len(motif) - 1 while j >= 0 and texte[i+j] == motif[j] : j -= 1 if j == -1: positions.append(i) i += ... else: if ... : i += ... else: i += ... return positions
-
Reprendre les mesures de temps d'éxécution et comparer avec la recherche naïve.