Conozco varios algoritmos básicos de coincidencia de cadenas como KMP o Boyer-Moore, pero todos analizan el patrón antes de buscar. Sin embargo, si uno tiene un solo carácter, no hay mucho para analizar. Entonces, ¿hay algún algoritmo mejor que la ingenua búsqueda de comparar cada carácter del texto?
algorithms
string-matching
cristiano
fuente
fuente
Respuestas:
Se entiende que el peor de los casos es que
O(N)
hay algunas micro optimizaciones muy buenas.El método ingenuo realiza una comparación de caracteres y una comparación de final de texto para cada carácter.
El uso de un centinela (es decir, una copia del carácter objetivo al final del texto) reduce el número de comparaciones a 1 por carácter.
En el nivel de twiddling hay:
para saber si algún byte en una palabra (
x
) tiene un valor específico (n
).La subexpresión se
v - 0x01010101UL
evalúa como un conjunto de bits alto en cualquier byte siempre que el byte correspondientev
sea cero o mayor que0x80
.La subexpresión se
~v & 0x80808080UL
evalúa en bits altos establecidos en bytes donde el byte dev
no tiene su conjunto de bits alto (por lo que el byte era menor que0x80
).Al ANDing estas dos subexpresiones (
haszero
), el resultado es el conjunto de bits altos donde los bytes env
eran cero, ya que los bits altos establecidos debido a un valor mayor que0x80
en la primera subexpresión se enmascaran en la segunda (27 de abril, 1987 por Alan Mycroft).Ahora podemos XOR el valor para probar (
x
) con una palabra que se ha llenado con el valor de byte en el que estamos interesados (n
). Debido a que XORing un valor consigo mismo da como resultado un byte cero y distinto de cero, podemos pasar el resultado ahaszero
.Esto se usa a menudo en una
strchr
implementación típica .(Stephen M Bennet sugirió esto el 13 de diciembre de 2009. Más detalles en los conocidos Bit Twiddling Hacks ).
PD
El truco pasa la prueba de fuerza bruta (solo sea paciente):
Gracias por el comentario
La respuesta debía ser cualquier cosa menos un ensayo sobre codificaciones de varios bytes / ancho variable :-) (para ser justos, esa no es mi área de especialización y no estoy seguro de que sea lo que estaba buscando el OP).
De todos modos, me parece que las ideas / trucos anteriores podrían adaptarse de alguna manera al MBE (especialmente las codificaciones de sincronización automática ):
strchr
/strstr
(por ejemplo, GNUlib coreutils mbschr )fuente
0x01010101UL
en una línea y~0UL / 255
en la siguiente. Da la impresión de que deben ser valores diferentes, ya que de lo contrario, ¿por qué escribirlo de dos maneras diferentes?#define
s se expandiría a( (((x) ^ (0x01010101UL * (n)))) - 0x01010101UL) & ~((x) ^ (0x01010101UL * (n)))) & 0x80808080UL )
. ¿No sería la comparación de un solo byte más rápida?Cualquier algoritmo de búsqueda de texto que busque cada aparición de un solo carácter en un texto dado tiene que leer cada carácter del texto al menos una vez, eso debería ser obvio. Y dado que esto es suficiente para una búsqueda única, no puede haber un algoritmo mejor (cuando se piensa en términos de orden de tiempo de ejecución, que se llama "lineal" u O (N) para este caso, donde N es el número de caracteres para buscar a través de).
Sin embargo, para implementaciones reales, seguramente hay muchas microoptimizaciones posibles, que no cambian el orden del tiempo de ejecución en general, sino que reducen el tiempo de ejecución real. Y si el objetivo no es encontrar cada aparición de un solo personaje, sino solo la primera, puede detenerse en la primera aparición, por supuesto. Sin embargo, incluso para ese caso, el peor de los casos es que el personaje que está buscando es el último personaje del texto, por lo que el orden de tiempo de ejecución del peor caso para este objetivo sigue siendo O (N).
fuente
Si se busca su "pajar" más de una vez, un enfoque basado en histograma será extremadamente rápido. Después de construir el histograma, solo necesita una búsqueda de puntero para encontrar su respuesta.
Si solo necesita saber si el patrón buscado está presente, un simple contador puede ayudar. Se puede extender para incluir las posiciones en las que se encuentra cada personaje en el pajar, o la posición de la primera ocurrencia.
fuente
Si necesita buscar caracteres en esta misma cadena más de una vez, entonces un posible enfoque es dividir la cadena en partes más pequeñas, posiblemente de manera recursiva, y usar filtros de floración para cada una de estas partes.
Dado que un filtro de floración puede decirle con seguridad si un carácter no está en la parte de la cadena que está "representada" por el filtro, puede omitir algunas partes mientras busca caracteres.
Como ejemplo: para la siguiente cadena, se podría dividir en 4 partes (cada una de 11 caracteres de longitud) y llenar para cada parte un filtro de floración (quizás de 4 bytes) con los caracteres de esa parte:
Puede acelerar su búsqueda, por ejemplo, para el personaje
a
: utilizando buenas funciones hash para los filtros de floración, le dirán que, con alta probabilidad, no tiene que buscar ni en la primera, segunda ni tercera parte. Por lo tanto, se ahorra la comprobación de 33 caracteres y, en cambio, solo tiene que comprobar 16 bytes (para los 4 filtros de floración). Esto sigue siendoO(n)
, solo con un factor constante (fraccionario) (y para que esto sea efectivo, deberá elegir partes más grandes, para minimizar la sobrecarga de calcular las funciones hash para el carácter de búsqueda).El uso de un enfoque recursivo en forma de árbol debería acercarte
O(log n)
:En esta configuración, uno necesita (nuevamente, suponiendo que tuvimos suerte y no obtuvimos un falso positivo de uno de los filtros) para verificar
para llegar a la parte final (donde hay que verificar 3 caracteres hasta encontrar el
a
).Usando un buen esquema de subdivisión (mejor que el anterior), debería obtener resultados bastante buenos con eso. (Nota: los filtros Bloom en la raíz del árbol deben ser más grandes que cerca de las hojas, como se muestra en el ejemplo, para obtener una baja probabilidad de falsos positivos)
fuente
Si la cadena se va a buscar varias veces (problema típico de "búsqueda"), la solución puede ser O (1). La solución es construir un índice.
P.ej :
Mapa, donde la clave es el carácter y el valor es una lista de índices para ese carácter en la cadena.
Con esto, una sola búsqueda en el mapa puede proporcionar la respuesta.
fuente