Je connais plusieurs algorithmes de correspondance de chaînes de base tels que KMP ou Boyer-Moore, mais tous analysent le modèle avant de rechercher.Cependant, si l'on n'a qu'un seul caractère, il n'y a pas grand-chose à analyser. Existe-t-il donc un meilleur algorithme que la recherche naïve de comparaison de chaque caractère du texte?
algorithms
string-matching
Christian
la source
la source
Réponses:
Étant entendu que le pire des cas est
O(N)
, il y a de très belles micro-optimisations.La méthode naïve effectue une comparaison de caractères et une comparaison de fin de texte pour chaque caractère.
L'utilisation d'une sentinelle (c'est-à-dire une copie du caractère cible à la fin du texte) réduit le nombre de comparaisons à 1 par caractère.
Au niveau du bit twiddling, il y a:
pour savoir si un octet dans un mot (
x
) a une valeur spécifique (n
).La sous
v - 0x01010101UL
- expression est évaluée à un bit élevé défini dans n'importe quel octet chaque fois que l'octet correspondant dansv
est nul ou supérieur à0x80
.La sous-expression est
~v & 0x80808080UL
évaluée à des bits élevés définis en octets où l'octet dev
n'a pas son bit élevé défini (donc l'octet était inférieur à0x80
).En ANDant ces deux sous-expressions (
haszero
), le résultat est l'ensemble des bits élevés où les octetsv
étaient nuls, car les bits élevés définis en raison d'une valeur supérieure à celle0x80
de la première sous-expression sont masqués par la seconde (27 avril, 1987 par Alan Mycroft).Maintenant, nous pouvons XOR la valeur à tester (
x
) avec un mot qui a été rempli avec la valeur d'octet qui nous intéresse (n
). Étant donné que XOR une valeur avec elle-même entraîne un octet nul et sinon différent de zéro, nous pouvons transmettre le résultat àhaszero
.Ceci est souvent utilisé dans une
strchr
implémentation typique .(Stephen M Bennet l'a suggéré le 13 décembre 2009. Plus de détails dans les fameux Bit Twiddling Hacks ).
PS
Le hack passe le test de force brute (soyez patient):
Merci pour la remarque.
La réponse devait être autre chose qu'un essai sur les codages multi-octets / à largeur variable :-) (en toute honnêteté, ce n'est pas mon domaine d'expertise et je ne suis pas sûr que ce soit ce que l'OP recherchait).
Quoi qu'il en soit, il me semble que les idées / astuces ci-dessus pourraient être quelque peu adaptées au MBE (en particulier les encodages auto-synchronisés ):
strchr
/strstr
(par exemple GNUlib coreutils mbschr )la source
0x01010101UL
sur une ligne et~0UL / 255
sur la suivante. Cela donne l'impression qu'il doit s'agir de valeurs différentes, car sinon, pourquoi l'écrire de deux manières différentes?#define
s s'étendrait jusqu'à( (((x) ^ (0x01010101UL * (n)))) - 0x01010101UL) & ~((x) ^ (0x01010101UL * (n)))) & 0x80808080UL )
. La comparaison sur un octet ne serait-elle pas plus rapide?Tout algorithme de recherche de texte qui recherche chaque occurrence d'un seul caractère dans un texte donné doit lire chaque caractère du texte au moins une fois, cela devrait être évident. Et comme cela suffit pour une recherche unique, il ne peut y avoir de meilleur algorithme (en pensant en termes d'ordre d'exécution, qui est appelé "linéaire" ou O (N) dans ce cas, où N est le nombre de caractères rechercher).
Cependant, pour les implémentations réelles, il y a sûrement beaucoup de micro-optimisations possibles, qui ne modifient pas l'ordre du temps d'exécution dans son ensemble, mais réduisent le temps d'exécution réel. Et si le but n'est pas de trouver chaque occurrence d'un seul personnage, mais seulement la première, vous pouvez bien sûr vous arrêter à la première occurrence. Néanmoins, même dans ce cas, le pire des cas est toujours que le caractère que vous recherchez est le dernier caractère du texte, donc l'ordre d'exécution du pire des cas pour cet objectif est toujours O (N).
la source
Si votre "meule de foin" est recherchée plus d'une fois, une approche basée sur l'histogramme va être extrêmement rapide. Une fois l'histogramme créé, il vous suffit de rechercher un pointeur pour trouver votre réponse.
Si vous avez seulement besoin de savoir si le motif recherché est présent, un simple compteur peut vous aider. Il peut être étendu pour inclure la ou les positions auxquelles chaque personnage se trouve dans la botte de foin, ou la position de la première occurrence.
la source
Si vous devez rechercher plusieurs fois des caractères dans cette même chaîne, une approche possible consiste à diviser la chaîne en parties plus petites, éventuellement de manière récursive, et à utiliser des filtres de bloom pour chacune de ces parties.
Puisqu'un filtre de floraison peut vous dire avec certitude si un caractère ne se trouve pas dans la partie de la chaîne qui est "représentée" par le filtre, vous pouvez ignorer certaines parties lors de la recherche de caractères.
À titre d'exemple: pour la chaîne suivante, on pourrait la diviser en 4 parties (chacune de 11 caractères de long), et remplir pour chaque partie un filtre de bloom (peut-être 4 octets de large) avec les caractères de cette partie:
Vous pouvez accélérer votre recherche, par exemple pour le personnage
a
: en utilisant de bonnes fonctions de hachage pour les filtres de floraison, ils vous diront que - avec une forte probabilité - vous n'avez pas à chercher dans la première, la deuxième ou la troisième partie. Ainsi, vous vous évitez de vérifier 33 caractères et vous n'avez qu'à vérifier 16 octets (pour les 4 filtres de floraison). C'est toujoursO(n)
, juste avec un facteur (fractionnel) constant (et pour que cela soit efficace, vous devrez choisir des parties plus grandes, afin de minimiser la surcharge de calcul des fonctions de hachage pour le caractère de recherche).L'utilisation d'une approche récursive et arborescente devrait vous rapprocher
O(log n)
:Dans cette configuration, il faut (encore une fois, en supposant que nous avons eu de la chance et que nous n'avons pas obtenu de faux positif de l'un des filtres) pour vérifier
pour arriver à la dernière partie (où il faut vérifier 3 caractères jusqu'à trouver le
a
).En utilisant un bon schéma de subdivision (meilleur que celui ci-dessus), vous devriez obtenir de très bons résultats avec cela. (Remarque: les filtres de floraison à la racine de l'arbre doivent être plus grands que près des feuilles, comme indiqué dans l'exemple, pour obtenir une faible probabilité de faux positifs)
la source
Si la chaîne doit être recherchée plusieurs fois (problème de "recherche" typique), la solution peut être O (1). La solution est de construire un index.
Par exemple :
Carte, où Key est le caractère et Value est une liste d'index pour ce caractère dans la chaîne.
Avec cela, une seule recherche de carte peut fournir la réponse.
la source