OK, então eu não pareço um idiota, vou declarar o problema / requisitos mais explicitamente:
- Agulha (padrão) e palheiro (texto a ser pesquisado) são seqüências terminadas em nulo no estilo C. Nenhuma informação de comprimento é fornecida; se necessário, deve ser calculado.
- A função deve retornar um ponteiro para a primeira correspondência, ou
NULL
se nenhuma correspondência for encontrada. - Casos de falha não são permitidos. Isso significa que qualquer algoritmo com requisitos de armazenamento não constante (ou grande constante) precisará ter um caso de fallback para falha de alocação (e o desempenho no cuidado de fallback contribui para o pior desempenho).
- A implementação deve ser em C, embora uma boa descrição do algoritmo (ou link para tal) sem código também seja adequada.
... bem como o que quero dizer com "mais rápido":
- Determinístico
O(n)
onden
= comprimento do palheiro. (Mas pode ser possível usar idéias de algoritmos que são normalmenteO(nm)
(por exemplo, hash rotativo) se elas forem combinadas com um algoritmo mais robusto para fornecerO(n)
resultados determinísticos ). - Nunca apresenta desempenho (mensurável; alguns relógios
if (!needle[1])
são aceitáveis) pior que o algoritmo ingênuo de força bruta, especialmente em agulhas muito curtas, que provavelmente são o caso mais comum. (A sobrecarga pesada de pré-processamento incondicional é ruim, pois está tentando melhorar o coeficiente linear para agulhas patológicas às custas de prováveis agulhas.) - Dada uma agulha e um palheiro arbitrários, desempenho comparável ou melhor (não inferior a 50% do tempo de pesquisa) em comparação com qualquer outro algoritmo amplamente implementado.
- Além dessas condições, estou deixando a definição de "mais rápido" em aberto. Uma boa resposta deve explicar por que você considera a abordagem sugerida como "mais rápida".
Minha implementação atual é aproximadamente 10% mais lenta e 8 vezes mais rápida (dependendo da entrada) do que a implementação de duas vias da glibc.
Atualização: Meu algoritmo ideal atual é o seguinte:
- Para agulhas de comprimento 1, use
strchr
. - Para agulhas de comprimento 2 a 4, use palavras de máquina para comparar 2 a 4 bytes de uma vez da seguinte maneira: Pré-carregue a agulha em um número inteiro de 16 ou 32 bits com deslocamento de bits e faça o ciclo de saída de bytes antigos / novos bytes do palheiro a cada iteração . Cada byte do palheiro é lido exatamente uma vez e incorre em uma verificação contra 0 (final da string) e uma comparação de 16 ou 32 bits.
- Para agulhas de comprimento> 4, use o algoritmo Bidirecional com uma tabela de deslocamento ruim (como Boyer-Moore), que é aplicada apenas ao último byte da janela. Para evitar a sobrecarga de inicializar uma tabela de 1kb, o que seria uma perda líquida para muitas agulhas de comprimento moderado, mantenho uma matriz de bits (32 bytes) marcando quais entradas na tabela de deslocamento são inicializadas. Os bits não configurados correspondem aos valores de bytes que nunca aparecem na agulha, para os quais é possível uma mudança no comprimento total da agulha.
As grandes questões que me restam são:
- Existe uma maneira de fazer melhor uso da tabela de turnos ruim? A Boyer-Moore faz o melhor uso possível, digitalizando para trás (da direita para a esquerda), mas o Two-Way exige uma digitalização da esquerda para a direita.
- Os únicos dois algoritmos candidatos viáveis que encontrei para o caso geral (sem condições de desempenho quadrático ou de falta de memória) são a Correspondência de duas vias e a seqüência de caracteres em alfabetos ordenados . Mas existem casos facilmente detectáveis em que algoritmos diferentes seriam ótimos? Certamente muitos dos algoritmos
O(m)
(ondem
está o comprimento da agulha) no espaço podem ser usados param<100
isso. Também seria possível usar algoritmos quadráticos, na pior das hipóteses, se houver um teste fácil para agulhas que provavelmente requer apenas tempo linear.
Pontos de bônus por:
- Você pode melhorar o desempenho assumindo que a agulha e o palheiro são UTF-8 bem formados? (Com caracteres de tamanhos variáveis de bytes, a boa formação impõe alguns requisitos de alinhamento de cordas entre a agulha e o palheiro e permite trocas automáticas de 2-4 bytes quando um byte incompatível é encontrado. Mas essas restrições compram muito / qualquer coisa além do que cálculos máximos de sufixos, boas mudanças de sufixos, etc. já oferecem vários algoritmos?)
Nota: Conheço bem a maioria dos algoritmos existentes, mas não o desempenho deles na prática. Aqui está uma boa referência para que as pessoas não continuem me fornecendo referências sobre algoritmos como comentários / respostas: http://www-igm.univ-mlv.fr/~lecroq/string/index.html
strstr
como algo para mais tarde, então eu realmente não consegui ler corretamente o artigo que você vinculou, mas parece muito promissor. Obrigado e desculpe por não voltar para você.