OK, pour ne pas passer pour un idiot, je vais énoncer le problème/les exigences de manière plus explicite :
- Needle (motif) et haystack (texte à rechercher) sont tous deux des chaînes de caractères à terminaison nulle de style C. Aucune information sur la longueur n'est fournie ; si nécessaire, elle doit être calculée.
- La fonction doit retourner un pointeur vers la première correspondance, ou
NULL
si aucune correspondance n'est trouvée. - Les cas d'échec ne sont pas autorisés. Cela signifie que tout algorithme ayant des besoins de stockage non constants (ou constants importants) devra avoir un cas de repli en cas d'échec de l'allocation (et les performances du cas de repli contribuent ainsi aux performances du pire cas).
- L'implémentation doit être en C, mais une bonne description de l'algorithme (ou un lien vers celle-ci) sans code est également acceptable.
...ainsi que ce que j'entends par "le plus rapide" :
- Déterministe
O(n)
donden
= longueur de la meule de foin. (Mais il peut être possible d'utiliser des idées provenant d'algorithmes qui sont normalementO(nm)
(par exemple le rolling hash) s'ils sont combinés avec un algorithme plus robuste pour donner des résultats déterministes.O(n)
résultats). - Ne fonctionne jamais (de manière mesurable ; quelques horloges pour les
if (!needle[1])
etc. sont acceptables) pire que l'algorithme naïf de force brute, en particulier sur les aiguilles très courtes qui sont probablement le cas le plus courant. (Des frais de prétraitement lourds et inconditionnels sont mauvais, tout comme le fait d'essayer d'améliorer le coefficient linéaire pour les aiguilles pathologiques au détriment des aiguilles probables). - Pour une aiguille et une botte de foin arbitraires, des performances comparables ou supérieures (pas moins de 50% de temps de recherche en plus) à celles de tout autre algorithme largement implémenté.
- En dehors de ces conditions, je laisse la définition de "plus rapide" ouverte. Une bonne réponse devrait expliquer pourquoi vous considérez l'approche que vous proposez comme "la plus rapide".
Mon implémentation actuelle tourne en gros entre 10% plus lentement et 8 fois plus vite (selon l'entrée) que l'implémentation de la glibc pour Two-Way.
Mise à jour : Mon algorithme optimal actuel est le suivant :
- Pour les aiguilles de longueur 1, utilisez
strchr
. - Pour les aiguilles de longueur 2-4, utilisez des mots machine pour comparer 2-4 octets à la fois comme suit : Préchargez l'aiguille dans un entier de 16 ou 32 bits avec des décalages de bits et faites sortir l'ancien octet et entrer les nouveaux octets de la botte de foin à chaque itération. Chaque octet de la botte de foin est lu exactement une fois et subit une vérification de 0 (fin de chaîne) et une comparaison de 16 ou 32 bits.
- Pour les aiguilles de longueur >4, utiliser l'algorithme Two-Way avec une mauvaise table de décalage (comme Boyer-Moore) qui est appliquée uniquement au dernier octet de la fenêtre. Pour éviter la surcharge de l'initialisation d'une table de 1kb, qui serait une perte nette pour beaucoup d'aiguilles de longueur moyenne, je garde un tableau de bits (32 octets) marquant quelles entrées dans la table de décalage sont initialisées. Les bits qui ne sont pas initialisés correspondent à des valeurs d'octets qui n'apparaissent jamais dans l'aiguille, pour lesquelles un décalage de toute la longueur de l'aiguille est possible.
Les grandes questions qui restent dans mon esprit sont :
- Existe-t-il un moyen de mieux utiliser la mauvaise table de décalage ? Boyer-Moore l'utilise au mieux en balayant à l'envers (de droite à gauche) mais Two-Way nécessite un balayage de gauche à droite.
- Les deux seuls algorithmes candidats viables que j'ai trouvés pour le cas général (pas de conditions de performance hors mémoire ou quadratiques) sont les suivants Deux voies y Correspondance de chaînes de caractères sur des alphabets ordonnés . Mais existe-t-il des cas facilement détectables où des algorithmes différents seraient optimaux ? Il est certain que de nombreux
O(m)
(oùm
est la longueur de l'aiguille) dans les algorithmes spatiaux pourraient être utilisés pourm<100
ou plus. Il serait également possible d'utiliser des algorithmes qui sont quadratiques dans le pire des cas s'il existe un test facile pour les aiguilles qui ne demandent qu'un temps linéaire.
Des points bonus pour :
- Pouvez-vous améliorer les performances en supposant que l'aiguille et la botte de foin sont toutes deux des UTF-8 bien formés (avec des caractères de différentes longueurs d'octets, le fait d'être bien formé impose certaines exigences d'alignement de chaîne entre l'aiguille et la botte de foin et permet des décalages automatiques de 2 à 4 octets lorsqu'un octet de tête non conforme est rencontré. Mais ces contraintes vous apportent-elles beaucoup/quelque chose de plus que ce que les calculs de suffixes maximaux, les bons décalages de suffixes, etc. vous donnent déjà avec divers algorithmes) ?
Note : Je suis bien conscient de la plupart des algorithmes existants, mais pas de leurs performances dans la pratique. Voici une bonne référence pour que les gens ne continuent pas à me donner des références sur les algorithmes dans leurs commentaires/réponses : http://www-igm.univ-mlv.fr/~lecroq/string/index.html