Algorithme de Boyer-Moore
En informatique, plus précisément en algorithmique, l'algorithme de Boyer-Moore est un algorithme de recherche de sous-chaßne particuliÚrement efficace, qui est utilisé comme référence avec lequel on compare d'autres algorithmes quand on réalise des expériences de recherche de sous-chaßne[1]. Il a été développé par Robert S. Boyer et J Strother Moore[2] en 1977.
Efficacité / complexité en temps
L'algorithme de Boyer-Moore prĂ©-traite la sous-chaĂźne « clĂ© » (c'est-Ă -dire la chaĂźne recherchĂ©e), et non pas le texte (c'est-Ă -dire la chaĂźne dans laquelle la recherche est effectuĂ©e), Ă l'inverse de certains algorithmes, qui amortissent le coĂ»t du prĂ©traitement du texte en effectuant de trĂšs nombreuses recherches rĂ©pĂ©titives. Le coĂ»t d'exĂ©cution de l'algorithme de Boyer-Moore peut ĂȘtre sous-linĂ©aire, c'est-Ă -dire qu'il n'a pas besoin de vĂ©rifier chacun des caractĂšres du texte, mais peut au contraire sauter certains d'entre eux. En gĂ©nĂ©ral, l'algorithme devient plus rapide lorsque la longueur de la sous-chaĂźne s'allonge. Cette efficacitĂ© provient du fait que, pour chaque tentative infructueuse de correspondance entre les deux chaĂźnes (texte et sous-chaĂźne), il utilise les informations dĂ©duites de cet Ă©chec pour Ă©liminer le plus grand nombre possible de positions Ă vĂ©rifier.
En comparaison des mĂ©thodes de recherches basiques par la mĂ©thode de « force brute » (qui recherche la sous-chaĂźne en commençant par chercher partout dans le texte une correspondance du premier caractĂšre de la sous-chaĂźne, puis en vĂ©rifiant si les caractĂšres suivants correspondent) et dont la complexitĂ© en temps (calculĂ©e par exemple selon le nombre de paires de caractĂšres Ă comparer) croit en O(L · K) oĂč K est la longueur de la sous-chaĂźne clĂ© recherchĂ©e, et L la longueur de la sequence oĂč lâon recherche sâil existe au moins une occurrence de la clĂ©, lâalgorithme de Boyer-Moore peut trouver les occurrences en un temps :
- De lâordre de O(L / K) dans le cas le plus favorable : dans ce meilleur cas, seul un caractĂšre sur M doit ĂȘtre vĂ©rifiĂ©. Ainsi, plus la sous-chaĂźne est longue, et plus lâalgorithme est efficace pour la trouver ;
- de lâordre de O(L + K) dans le pire cas (en utilisant la variante amĂ©liorĂ©e de lâalgorithme avec la seconde table de vĂ©rification des occurrences).
- et trĂšs souvent en un temps Ă peine supĂ©rieur au meilleur cas (qui devient de plus en plus probable quand la longueur K de la clĂ© sâaccroĂźt). Le pire cas est obtenu quand le texte contient de trĂšs nombreuses occurrences dâun mĂȘme caractĂšre, et quand la clĂ© cherchĂ©e contient ce caractĂšre frĂ©quent de nombreuses fois en fin de chaine sauf pour le premier caractĂšre qui est diffĂ©rent.
Le cas moyen pour Ă©tablir sâil y a correspondance ou non requiert environ (3 · K) comparaisons. La preuve est due Ă Richard Cole[3].
Dans le pire cas, les performances de l'algorithme de base (sans la variante avec la seconde table de vĂ©rification des occurrences) pour trouver toutes les correspondances est de l'ordre de O(K · L). Le pire cas se produit quand la sous-chaĂźne consiste en une rĂ©pĂ©tition dâun unique caractĂšre, et que le texte correspond Ă la rĂ©pĂ©tition de (K - 1) fois ce mĂȘme caractĂšre, prĂ©cĂ©dĂ© par un seul autre caractĂšre diffĂ©rent. Dans ce cas de figure, lâalgorithme doit vĂ©rifier (L - K+ 1) dĂ©calages diffĂ©rents dans le texte pour chaque correspondance. Or, chacune de ces vĂ©rifications nĂ©cessite K comparaisons.
En raison de lâintĂ©rĂȘt de la variante avec la seconde table qui permet un comportement sous-linĂ©aire mĂȘme dans le pire cas, cette variante est dĂ©crite ici (et est celle aujourd'hui intĂ©grĂ©e dans de nombreuses bibliothĂšques de traitement du texte pour C/C++, Java, etc.).
Fonctionnement
Principe
L'algorithme de Boyer-Moore est assez surprenant car il effectue la vérification, c'est-à -dire qu'il tente d'établir la correspondance de la sous-chaßne à une certaine position, à l'envers. Par exemple, s'il commence la recherche de la sous-chaßne WIKIPEDIA au début d'un texte, il vérifie d'abord la neuviÚme position en regardant si elle contient un A. Ensuite, s'il a trouvé un A, il vérifie la huitiÚme position pour regarder si elle contient le dernier I de la sous-chaßne, et ainsi de suite jusqu'à ce qu'il ait vérifié la premiÚre position du texte pour y trouver un W.
La raison pour laquelle l'algorithme de Boyer-Moore utilise cette approche à rebours est plus claire si l'on considÚre ce qui se produit quand la vérification échoue, par exemple si au lieu de trouver un A en neuviÚme position, un X est lu. Le X n'apparaßt nulle part dans la sous-chaßne WIKIPEDIA, ce qui signifie qu'aucune correspondance avec la sous-chaßne n'existe au tout début du texte, ainsi que dans les huit positions qui la suivent. AprÚs la vérification d'un seul caractÚre, l'algorithme est capable de passer ces huit caractÚres et de rechercher le début d'une correspondance à partir de la dixiÚme position dans le texte, juste aprÚs le X.
Ce principe de fonctionnement Ă rebours explique la quelque peu contre-intuitive affirmation disant que plus la sous-chaĂźne est longue, et plus l'algorithme est efficace pour la trouver.
Exemple
Prenons un exemple plus significatif : on veut rechercher les occurrences de la clĂ© de K=6 caractĂšres âstringâ dans le texte de L=20 caractĂšres âstupid_spring_string".
- Avec un algorithme classique utilisant une mĂ©thode naĂŻve, on devrait rechercher le s dans tout le texte sauf les 5 derniers caractĂšres, et donc faire toutes les 15 comparaisons. Et on trouverait 3 occurrences du s au dĂ©but de chaque mot du texte ; pour chacune de ces occurrences on devrait vĂ©rifier la suite de la clĂ© tant qu'elle correspond : on dĂ©tecterait le p une fois pour rejeter lâoccurrence commençant par spring, mais le t serait dĂ©tectĂ© 2 fois dans stupid et string ; on doit alors tester la prĂ©sence du r aprĂšs st pour Ă©liminer lâoccurrence dans stupid ; il reste encore Ă vĂ©rifier chacun des 3 autres caractĂšres de la clĂ© string, il faudra donc 23 comparaisons (ce serait pire si le texte contenait de nombreuses occurrences de la quasi-totalitĂ© du dĂ©but de la clĂ©).
Avec lâalgorithme de Boyer-Moore, on recherchera aussi les occurrences depuis le dĂ©but du texte (en affichant ci-dessous la clĂ© cherchĂ©e en dessous du texte balayĂ©, les points notĂ©s avant la clĂ© indiquant les positions dĂ©jĂ Ă©liminĂ©es, le surlignage indique les comparaisons effectuĂ©es), mais on commencera en comparant le dernier caractĂšre de la clĂ© cherchĂ©e :
stupid_spring_string
string
Les deux caractĂšres d et g ne correspondent pas, on a trouvĂ© Ă la place un d dans le texte, alors quâil nây a aucun d dans la clĂ©. On peut sauter directement dans le texte les 6 caractĂšres de la clĂ© :
stupid_spring_string
······string
Le g de la clĂ© ne correspond pas au n du texte. Cependant, la clĂ© contient un n, 1 caractĂšre avant, on peut donc dĂ©caler dâ1 position, et vĂ©rifier Ă nouveau en commençant par la fin de clĂ© :
stupid_spring_string
·······string
On a trouvé successivement la correspondance du g, puis du n, puis du i, puis du r, mais pas du t de la clé. Au lieu de cela on a trouvé un p dans le texte, qui ne figure pas dans la clé, ce qui permet de décaler de 2 caractÚres pour placer le début de la clé aprÚs le p:
stupid_spring_string
·········string
Le g de la clé ne correspond pas au s du texte. Cependant, la clé contient un s, 5 caractÚres avant, on peut donc décaler de 5 positions, et vérifier à nouveau en commençant par la fin de clé :
stupid_spring_string
··············string
On trouve successivement les correspondances du g, puis du n, du i, du r, du t et du s. Il n'y a plus dâautre caractĂšre dans la clĂ©, on a bien trouvĂ© une occurrence en seulement 14 comparaisons (au lieu de 23 avec lâalgorithme classique). Si on devait chercher les occurrences suivantes, il suffit de reprendre lâalgorithme dans le texte Ă partir de la position qui suit la position trouvĂ©e.
Contraintes dâimplĂ©mentation
Il faut noter que, pour que lâalgorithme de Boyer-Moore puisse fonctionner de façon efficace, il est nĂ©cessaire de pouvoir parcourir le texte (ainsi que la clĂ© cherchĂ©e) en le parcourant linĂ©airement en sens inverse, et de pouvoir sauter directement Ă une position ultĂ©rieure du texte sans avoir Ă le lire intĂ©gralement entre les deux positions, ni Ă devoir relire le texte ou la clĂ© depuis le dĂ©but, et sans avoir Ă utiliser de coĂ»teux tampons mĂ©moire compliquĂ©s Ă gĂ©rer. Ce nâest pas toujours le cas de tous les flux de fichiers texte Ă lecture unidirectionnelle.
Et dans le cas oĂč la recherche doit utiliser des comparaisons basĂ©es sur la collation (en) de chaĂźnes conformes Ă des rĂšgles linguistiques dans lesquelles certaines diffĂ©rences sont ignorĂ©es dans la recherche des correspondances, les Ă©lĂ©ments Ă comparer ne seront pas les caractĂšres eux-mĂȘmes mais les Ă©lĂ©ments de collation prĂ©calculĂ©s sur la clĂ© et ceux obtenus au fil de lâeau dans le texte, dans lequel il doit ĂȘtre nĂ©cessaire de dĂ©terminer si les positions sont celles marquant la sĂ©paration des Ă©lĂ©ments de collation (afin de ne pas trouver de faux positifs ni oublier des correspondances lorsquâon saute directement certaines positions ou quand on lit le texte ou la clĂ© en sens inverse) : cela pose certaines difficultĂ©s dans les collations linguistiques comprenant des contractions et expansions, ou encore dans des textes Unicode non normalisĂ©s pour lesquels plusieurs codages sont possibles. Mais des algorithmes complĂ©mentaires ont Ă©tĂ© dĂ©veloppĂ©s pour traiter efficacement cette difficultĂ© (et quelques autres liĂ©es Ă lâĂ©tendue du jeu de caractĂšres Unicode (ou lâĂ©tendue numĂ©rique encore plus grande des poids de collation multiniveau)[4].
Pré-traitement
Ă partir de la clĂ©, l'algorithme prĂ©calcule deux tableaux indiquant un nombre de positions Ă sauter aprĂšs chaque vĂ©rification dans le texte. Ces tableaux (parfois appelĂ©s « tables de sauts ») exploitent lâinformation tirĂ©e de la vĂ©rification effectuĂ©e.
- Le premier se base sur le caractĂšre du texte qui a Ă©tĂ© comparĂ© au dernier caractĂšre de la clĂ© (câest la premiĂšre comparaison effectuĂ©e puisque les vĂ©rifications se font de droite Ă gauche).
- Le second se base sur le nombre de caractÚres vérifiés avec succÚs (ce nombre peut égaler la taille de la clé, auquel cas la vérification a réussi).
PremiĂšre table de sauts (indicĂ©e par les lettres de lâalphabet)
Le premier tableau utilise le constat suivant : si on cherche par exemple la clé WIKIPEDIA dans le texte ENCYCLOPEDIA, alors la premiÚre vérification sera :
ENCYCLOPEDIA
WIKIPEDIA
Sachant que la premiĂšre lettre du texte quâon a comparĂ©e est un E, il est inutile de vĂ©rifier les appariements suivants :
ENCYCLOPEDIA
·WIKIPEDIA
ENCYCLOPEDIA
··WIKIPEDIA
On peut sauter directement Ă celui-ci :
ENCYCLOPEDIA
···WIKIPEDIA
Ici, on a donc avancĂ© de trois positions au lieu dâune seule, câest-Ă -dire la distance entre la derniĂšre lettre E dans la clĂ© et la fin de la clĂ©. Si cette lettre nâapparaissait pas dans la clĂ©, alors on aurait pu sauter toute la longueur de celle-ci.
La premiĂšre table de sauts est donc facile Ă calculer : elle associe Ă chaque caractĂšre de lâalphabet la distance, mesurĂ©e depuis la fin de la clĂ©, de la derniĂšre occurrence de cette lettre dans la clĂ©. La derniĂšre position de la clĂ© nâest pas comptĂ©e dans les occurrences ; autrement, la distance associĂ©e Ă la derniĂšre lettre de la clĂ© (la lettre A dans lâexemple) serait nulle et on resterait sur place aprĂšs lâavoir lue dans le texte. Les lettres nâapparaissant pas dans la clĂ© sont associĂ©es Ă la longueur de la clĂ©. Un algorithme de calcul est donc :
- pré-remplir toutes les valeurs du tableau avec la longueur de la clé ;
- dĂ©marrer de lâavant-derniĂšre position dans la clĂ© avec le compteur Ă 1, et aller vers le dĂ©but en incrĂ©mentant le compteur ;
- pour chaque position, si le caractĂšre courant nâest pas dĂ©jĂ dans le tableau, lâajouter avec la valeur actuelle du compteur.
Exemple : avec la clĂ© WIKIPEDIA, le premier tableau est rempli comme suit (pour plus de clartĂ©, les entrĂ©es sont donnĂ©es dans l'ordre oĂč elles sont ajoutĂ©es dans le tableau) :
CaractĂšre de lâalphabet | Distance Ă la fin de clĂ© |
---|---|
I | 1 |
D | 2 |
E | 3 |
P | 4 |
K | 6 |
W | 8 |
autres caractĂšres | 9 |
Le lecteur attentif remarquera que le A, le dernier caractĂšre de la clĂ©, nâa pas Ă©tĂ© ajoutĂ© dans le tableau. En effet, il ne prĂ©sente pas dâautre occurrence dans la clĂ©.
Notes de performance :
- Ce tableau a une taille (nombre total dâentrĂ©es) indĂ©pendante de la longueur de la clĂ©, mais proportionnelle Ă la taille de lâalphabet.
- Si lâalphabet est trĂšs grand (par exemple le rĂ©pertoire universel UCS dâUnicode et ISO/CEI 10646 dont lâalphabet comprend plus dâun million de points de code possibles) sa taille pourrait devenir prohibitive, alors que la plus grande partie de ce tableau contiendrait la valeur par dĂ©faut (la longueur de la clĂ©), et son prĂ©-remplissage peut prendre du temps. Cela peut sâoptimiser en remarquant que la table ne contient pas de valeur nulle. la valeur par dĂ©faut pourra donc ĂȘtre codĂ©e par 0 sans ambiguĂŻtĂ©. Cette astuce permet dâĂ©conomiser lâinitialisation du tableau dans un environnement qui prĂ©-remplit les tableaux Ă zĂ©ro.
- De plus, lâalgorithme de Boyer-Moore doit son efficacitĂ© Ă sa capacitĂ© de sauter des longueurs de texte suffisantes. Il nâest pas nĂ©cessaire de sauter la distance maximale, une distance raisonnablement grande (par rapport Ă la longueur de la clĂ©) suffit. Quand lâalphabet est bien plus grand que la clĂ©, lâalgorithme restera efficace si on rĂ©duit lâalphabet Ă des classes de caractĂšres (ou dâĂ©lĂ©ments de collation) avec une bonne rĂ©partition.
- Dans ce cas, le tableau ne sera pas indicĂ© par les caractĂšres mais par les classes de caractĂšres : une fonction de hachage simple rĂ©duisant ce grand alphabet Ă (par exemple) un ensemble rĂ©duit Ă 256 classes convenablement distribuĂ©es suffira et fonctionnera trĂšs efficacement pour des longueurs de clĂ© pouvant aller jusqu'Ă plusieurs milliers de caractĂšres, la table permettant alors dâeffectuer des sauts de 1 Ă 256 caractĂšres.
- En revanche, si lâalphabet est extrĂȘmement rĂ©duit (par exemple, un alphabet binaire), lâefficacitĂ© de lâalgorithme sera totalement nulle (par rapport Ă un algorithme de recherche naĂŻf) avec cette table de sauts. Lâastuce consistera Ă lire le texte et la clĂ© non pas caractĂšre par caractĂšre, mais par groupes de caractĂšres afin dâaugmenter lâalphabet Ă un cardinal suffisant. Par exemple, avec un alphabet binaire, on pourra lire par paquets de 8 caractĂšres, en fixant un caractĂšre de bourrage arbitraire (ou moins probable) pour les caractĂšres (du petit alphabet) qui manquent au dĂ©but de la clĂ© ou au dĂ©but du texte mais qui sont nĂ©cessaires Ă la formation de groupes complets de lettres convertis en lettres Ă©quivalentes du nouvel alphabet augmentĂ©. On tiendra ensuite compte, lorsque des correspondances sont trouvĂ©es entre des groupes de caractĂšres, du nombre de caractĂšres de bourrage utilisĂ©s pour ajuster la position du groupe trouvĂ©.
Seconde table de saut (indicée par la longueur de la clé comparée avec succÚs)
Le second tableau est sensiblement plus compliquĂ© Ă calculer : pour chaque valeur de N infĂ©rieure Ă la longueur K de la sous-chaĂźne clĂ©, il faut calculer le motif composĂ© des N derniers caractĂšres de la sous-chaĂźne K, prĂ©cĂ©dĂ© d'un caractĂšre qui ne correspond pas. Puis, il faut trouver le plus petit nombre de caractĂšres pour lesquels le motif partiel peut ĂȘtre dĂ©calĂ© vers la gauche avant que les deux motifs ne correspondent. Par exemple, pour la sous-chaĂźne clĂ© ANPANMAN longue de 8 caractĂšres, le tableau de 8 lignes est rempli de cette maniĂšre (les motifs dĂ©jĂ trouvĂ©s dans le texte sont montrĂ©s alignĂ©s dans des colonnes correspondant Ă lâĂ©ventuel motif suivant possible, pour montrer comment sâobtient la valeur de dĂ©calage qui est la seule rĂ©ellement calculĂ©e et stockĂ©e dans la seconde table de saut) :
Indice | Motif suivant | DĂ©calage obtenu | |||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
A | N | P | A | N | M | A | N | ||||||||
0 | 1 | ||||||||||||||
1 | N | 8 | |||||||||||||
2 | A | N | 3 | ||||||||||||
3 | M | A | N | 6 | |||||||||||
4 | N | M | A | N | 6 | ||||||||||
5 | A | N | M | A | N | 6 | |||||||||
6 | P | A | N | M | A | N | 6 | ||||||||
7 | N | P | A | N | M | A | N | 6 |
Notes relatives à la complexité de calcul cette table :
- On remarque que cette table contient autant de lignes que la longueur de clĂ©. Si la clĂ© est longue, les valeurs de dĂ©calage dans la table peuvent ĂȘtre elles aussi assez importantes, ce qui va nĂ©cessiter une allocation de mĂ©moire de travail peut-ĂȘtre importante, souvent plus grande en taille elle-mĂȘme que la chaĂźne clĂ© qui utilise un alphabet plus rĂ©duit (par exemple 1 octet par caractĂšre) que les entiers alors quâen fin de compte les longueurs de clĂ© seront importantes (typiquement des entiers codĂ©s sur 4 octets).
- La constitution de cette table nĂ©cessite lĂ aussi de rechercher des sous-chaĂźnes (toutes les sous-chaĂźnes possibles de la fin de clĂ©) pour en trouver pour chacune la derniĂšre occurrence dans la clĂ©, et lâalgorithme de Boyer-Moore pourrait ĂȘtre utilisĂ© rĂ©cursivement (mais en utilisant une recherche sur des textes et clĂ©s de direction inversĂ©e).
- Au-delĂ dâune certaine longueur raisonnable (par exemple jusquâaux 256 derniers caractĂšres de la clĂ©), il peut ĂȘtre inutile dâaugmenter la taille de cette table puisque le seul but sera de savoir si des dĂ©calages plus grands peuvent ĂȘtre obtenus pour sauter plus vite dans le texte principal. En ne prĂ©-traitant que les 256 derniers caractĂšres en fin de clĂ©, on obtiendra une longueur dĂ©jĂ suffisante pour accĂ©lĂ©rer la majoritĂ© des recherches (mais si on veut aller au-delĂ , on pourra, lorsque cette table contient dĂ©jĂ la longueur maximale retenue pour le saut et dans les rares cas oĂč cette longueur serait atteinte, et si lâalphabet est assez discriminant, ce que peut indiquer le taux de remplissage de la premiĂšre table, employer lâalgorithme pour rechercher par rĂ©cursion (plutĂŽt que par prĂ©-calcul dans cette table) les sous-chaĂźnes dans la clĂ©.
- Mais le plus simple est de borner les décalages à une valeur raisonnable comme 256 et ne pas chercher à aller au-delà . Dans ce cas, cette table aura une taille prédéfinie et ne coûtera rien en termes de complexité pour les clés trÚs longues, puisque son remplissage prendra un temps constant.
- DiffĂ©rentes stratĂ©gies sont possibles selon un compromis espace/temps (des stratĂ©gies dites « avec cache », ne font aucun prĂ©-calcul des tables, mĂȘme si elles en utilisent, mais remplissent cette table Ă la demande en fonction des longueurs de concordances effectivement trouvĂ©es Ă rebours lors de la vĂ©rification des occurrences finales dĂ©jĂ trouvĂ©es par la premiĂšre table ci-dessus).
Variantes
On donne ici le pseudo-code donné dans[5] qui est un autre algorithme que les auteurs appellent aussi algorithme de Boyer-Moore :
entrĂ©e : le motif m sortie : une table T tel que T[j][c] soit le plus grand k avec m[k] = c et k < j construireTable(m) T[0..|m|-1] := table oĂč chaque case est vide pour j = 0 Ă |m| - 1 T[j] := table oĂč chaque case pour chaque caractĂšre contient -1 pour k = 0 Ă j-1 T[j][m[k]] = k renvoyer T entrĂ©e : un motif t, un texte sortie : le nombre d'occurrences du motif t dans le texte BoyerMoore(m, texte) T = construireTable(m) compteur = 0 i = 0 tant que i <= |texte| - |m| k = 0 pour j = |m| - 1 Ă 0 en dĂ©crĂ©mentant de 1 Ă chaque pas si texte[i+j] != m[j] k = j - T[j][texte[i+j]] sortir de la boucle pour j si k = 0 occurrence trouvĂ©e Ă la position i compteur = compteur + 1 k = 1 i = i + k renvoyer compteur
La fonction construireTable(m) réalise le prétraitement. Elle calcule une table T avec pour tout position j dans le motif m et pour tout caractÚre c, T[j][c] est la plus grande position k dans le motif m avec m[k] = c et k < j. Une fois cette table T construite, l'algorithme de BoyerMoore compte le nombre d'occurrences de m dans le texte. La boucle tant que i parcourt toutes les positions i dans le texte. On parcourt les caractÚres du motif en partant de la droite. En cas de différence ( texte[i+j] != m[j] ), on retient le décalage trouvé dans k et on sort de la boucle (ce décalage vaut au moins 1). Une fois sorti de cette boucle pour interne, on fait face à une occurrence si et seulement si k = 0. Si tel est le cas, on augmente le compteur et on met k à 1. Puis on incrémente i de k.
Voir aussi
Articles connexes
Liens externes
- (en) Analyse, bibliographie et animation de l'algorithme de Boyer-Moore ;
- (en) Un exemple de l'algorithme de Boyer-Moore sur la page Internet de J Strother Moore, co-inventeur de l'algorithme.
Référence
- Andrew Hume et Daniel Sunday, « Fast String Searching », Software: Practice and Experience, vol. 21, no 11,â , p. 1221â1248 (DOI 10.1002/spe.4380211105, S2CID 5902579)
- Le prĂ©nom de « J Strother Moore » est bien la lettre J et non lâabrĂ©viation « J. » dâun prĂ©nom
- Tight bounds on the complexity of the Boyer-Moore algorithm, Proceedings of the 2d Annual ACM-SIAM Symposium on Discrete Algorithms, (1991)
- (en) Efficient text searching in Java, par Laura Werner, paru dans Java Report en 1999 (document présenté dans le projet ICU).
- Thibaut Balabonski, Sylvain Conchon, Jean-Christophe Filliùtre, Kim Nguyen, Laurent Sartre, Informatique MP2I/MPI: CPGE 1re et 2e années Cours et exercices corrigés, Ellipse, Chapitre 9, Section 9.5.1.1 p. 535-539