Cours : Algorithmique du texte
📌 1. Définitions & Cadre d’étude
Section titled “📌 1. Définitions & Cadre d’étude”Soit un alphabet fini, c’est-à-dire un ensemble non vide de symboles dits caractères. Un mot (ou texte) sur l’alphabet est une suite finie de caractères.
- L’ensemble de tous les mots sur est noté .
- Le mot vide, noté , est l’unique mot de longueur . La longueur d’un mot est notée .
- Si avec , un facteur (ou sous-chaîne) de est un sous-mot contigu pour .
Le problème central de l’algorithmique du texte est le problème de la recherche de motif (pattern matching) :
Étant donnés un texte de longueur et un motif de longueur (avec ), tous deux sur l’alphabet , déterminer toutes les occurrences de dans , c’est-à-dire les indices tels que .
📐 2. Théorèmes fondamentaux & Démonstrations clés
Section titled “📐 2. Théorèmes fondamentaux & Démonstrations clés”Théorème 1 : Complexité naive et complexité optimale
Section titled “Théorème 1 : Complexité naive et complexité optimale”L’algorithme naïf teste toutes les positions possibles de dans . Sa complexité temporelle dans le pire des cas est en , soit .
Il existe des algorithmes plus sophistiqués (KMP, Boyer-Moore, Rabin-Karp) ramenant la complexité dans le pire des cas à (ou lorsque ).
Démonstration / Analyse du pré-traitement de Knuth-Morris-Pratt (KMP)
Section titled “Démonstration / Analyse du pré-traitement de Knuth-Morris-Pratt (KMP)”Le cœur de l’algorithme de Knuth-Morris-Pratt repose sur la construction d’un tableau (souvent appelé tableau des préfixes stricts ou pi-table).
Soit . Pour tout , est défini comme la longueur du plus grand bord propre du préfixe . Un bord d’un mot est un mot qui est à la fois un préfixe et un suffixe propre de ce mot.
Formellement : Par convention, .
Lemme d’optimalité du calcul de
Section titled “Lemme d’optimalité du calcul de π\piπ”Le tableau peut être calculé en temps et en espace grâce à une approche incrémentale.
Démonstration (Esquisse) : Supposons que soient connus. Pour calculer , on pose .
- Si , alors .
- Si et , on réitère la comparaison en posant jusqu’à ce que ou .
- Si et , alors si , , sinon .
L’analyse amortie (via un potentiel mesurant l’indice ) montre que le nombre total de décrémentations de au cours du calcul de est borné par le nombre d’incrémentations, soit opérations au total.
💡 3. Méthodes de calcul & Algorithmes
Section titled “💡 3. Méthodes de calcul & Algorithmes”Algorithme de Knuth-Morris-Pratt (KMP)
Section titled “Algorithme de Knuth-Morris-Pratt (KMP)”L’algorithme utilise le tableau pour éviter de revenir en arrière dans le texte lors d’une non-concordance.
(* Recherche KMP en O(n + m) *)let kmp_search (p : string) (t : string) : int list = let m = String.length p in let n = String.length t in if m = 0 then [] else let pi = Array.make m 0 in (* Calcul du tableau pi *) let () = let j = ref 0 in for i = 1 to m - 1 do while !j > 0 && p.[i] <> p.[!j] do j := pi.[!j - 1] done; if p.[i] = p.[!j] then incr j; pi.[!i] <- !j done in (* Recherche proprement dite *) let results = ref [] in let j = ref 0 in for i = 0 to n - 1 do while !j > 0 && t.[i] <> p.[!j] do j := pi.[!j - 1] done; if t.[i] = p.[!j] then incr j; if !j = m then begin results := (i - m + 1) :: !results; j := pi.[m - 1] end done; List.rev !resultsAlgorithme de Rabin-Karp (Hachage glissant)
Section titled “Algorithme de Rabin-Karp (Hachage glissant)”L’algorithme de Rabin-Karp utilise une fonction de hachage glissante (rolling hash) pour calculer l’empreinte polynomiale de chaque facteur de longueur de en temps amorti après un calcul initial en .
Soit un alphabet dont les caractères sont identifiés à des entiers. Le hash d’un mot de longueur est défini par : où est la base (par exemple la taille de l’alphabet) et un nombre premier suffisamment grand.
Le passage du hash de au hash de se fait via la relation de récurrence en :
⚠️ 4. Pièges classiques aux Concours
Section titled “⚠️ 4. Pièges classiques aux Concours”-
Confusion indices 0-based et 1-based : Les formules de récurrence (notamment pour dans KMP ou les indices de fenêtres) varient selon que l’on manipule des tableaux indicés à partir de ou de . En entretien ou sur copie, explicitez toujours votre convention.
-
Dépassements entiers avec le hachage (Rabin-Karp) : En OCaml, les entiers standards (
int) sont signés sur 63 bits (en 64 bits). Lors du calcul de polynômes de hachage, des dépassements de capacité (overflows) non gérés mènent à des bugs silencieux. Il est impératif d’utiliser des calculs modulaires explicites ou le typeInt64. -
Complexité amortie vs Pire cas : Ne pas confondre la complexité dans le pire des cas (KMP : garanti) et la complexité probabiliste ou moyenne (Rabin-Karp : en moyenne, mais dans le pire des cas à cause des collisions de hachage si le nombre premier est mal choisi ou en présence d’un adversaire).
-
Invariants de boucle dans KMP : Oublier de maintenir l’invariant sur l’indice (le nombre de caractères déjà matchés) lors de la phase de rattrapage (
while !j > 0 && ...) conduit à des boucles infinies ou des indices hors limites (Invalid_argument). Veillez à bien écrire la mise à jour .