Probleme – Chercher motifs en une seule passe
Exercice · niveau 3 (difficile) · informatique (MP2I/MPI), chapitre 18 — Algorithmique des textes
Énoncé
Le chapitre annonce que Rabin-Karp cherche motifs de même longueur « pour presque le même prix », sans le faire.
- Écrire l'algorithme.
- Donner sa complexité, et la comparer à recherches indépendantes.
- Que faire si les motifs n'ont pas tous la même longueur ?
Corrigé
1. L'algorithme. On range les empreintes dans une table de hachage (chapitre chap:hachage), puis on fait glisser une seule fenêtre sur le texte.
/* Affiche les couples (position, motif) pour tous les motifs trouves.
Preconditions : les k motifs ont TOUS la longueur m, et n >= m >= 1.
Complexite : Theta(km) de precalcul, puis Theta(n) en moyenne. */
typedef struct maillon { long long h; const char* p; struct maillon* suiv; } maillon;
static maillon* table[TAILLE_TABLE];
void multi_rabin_karp(const char t[], int n, const char* motifs[], int k, int m) {
const long long B = 256, Q = 1000000007LL;
for (int i = 0; i < TAILLE_TABLE; i = i + 1) { table[i] = NULL; }
for (int i = 0; i < k; i = i + 1) { ajouter_motif(motifs[i], m); }
long long ht = 0, puissance = 1;
for (int j = 0; j < m - 1; j = j + 1) { puissance = (puissance * B) % Q; }
for (int j = 0; j < m; j = j + 1) { ht = (ht * B + (unsigned char) t[j]) % Q; }
for (int i = 0; i + m <= n; i = i + 1) {
/* on ne parcourt QUE le seau de l'empreinte courante */
for (maillon* x = table[ht % TAILLE_TABLE]; x != NULL; x = x->suiv) {
if (x->h == ht && strncmp(t + i, x->p, (size_t) m) == 0) {
printf("position %d : %s\n", i, x->p); /* on VERIFIE toujours */
}
}
if (i + m < n) {
ht = (ht - (unsigned char) t[i] * puissance % Q + Q) % Q;
ht = (ht * B + (unsigned char) t[i + m]) % Q;
}
}
}
Mesure sur un texte de caractères et les quatre motifs motif, texte, court, suite :
position 18 : motif position 88 : texte position 160 : motif
position 32 : texte position 119 : texte position 185 : motif
position 68 : motif position 133 : suite position 199 : texte
position 74 : court
total : 10 occurrences
Une seule passe, une seule empreinte roulante.
2. Les complexités.
| recherches séparées | une passe multi-motifs | |
|---|---|---|
| Précalcul | ||
| Parcours du texte | ||
| Vérifications (moyenne) | ||
| Mémoire |
Le gain est le facteur sur le parcours, et il est exactement ce que le chapitre annonce : le texte n'est lu qu'une fois. Sur un antivirus à signatures, cela fait la différence entre une seconde et une journée.
La condition à ne pas perdre de vue : le nombre de vérifications reste proportionnel à . Si est trop petit devant , chaque fenêtre déclenche une vérification et l'on retombe en . Le module doit croître avec le nombre de motifs, pas seulement avec leur longueur.
3. Des longueurs différentes. L'empreinte roulante suppose une fenêtre de taille fixe : on ne peut pas mélanger. Deux solutions.
- Grouper par longueur : une passe par longueur distincte. Si les motifs ont longueurs différentes, le coût devient au lieu de — intéressant dès que .
- Tronquer : ne hacher que les premiers caractères de chaque motif, où est la plus petite longueur. Une fenêtre qui correspond devient une candidate, que l'on vérifie sur toute la longueur du motif. Une seule passe, au prix de plus de vérifications.
La vraie réponse à ce problème est l'algorithme d'Aho-Corasick, hors programme, qui traite motifs de longueurs quelconques en dans le pire cas — en construisant un automate (chapitre chap:automates) sur l'ensemble des motifs. Rabin-Karp multi-motifs en est l'approximation probabiliste, et elle tient en trente lignes.
Les autres exercices de ce chapitre Le cours du chapitre
Un blocage sur cet exercice ? Le tuteur d'Adloun guide par questions, sans donner la réponse.