Entendendo-se que o pior caso é O(N), existem algumas micro-otimizações muito boas.
O método ingênuo realiza uma comparação de caracteres e uma comparação de final de texto para cada caractere.
O uso de uma sentinela (ou seja, uma cópia do caractere alvo no final do texto) reduz o número de comparações para 1 por caractere.
No nível de rodízio de bits, há:
#define haszero(v) ( ((v) - 0x01010101UL) & ~(v) & 0x80808080UL )
#define hasvalue(x, n) ( haszero((x) ^ (~0UL / 255 * (n))) )
para saber se algum byte em uma palavra ( x) tem um valor específico ( n).
A subexpressão é v - 0x01010101ULavaliada como um conjunto de bits alto em qualquer byte sempre que o byte correspondente vfor zero ou maior que 0x80.
A subexpressão é ~v & 0x80808080ULavaliada como bits altos definidos em bytes onde o byte de vnão possui seu conjunto de bits alto (portanto, o byte era menor que 0x80).
Ao ANDing essas duas subexpressões ( haszero), o resultado é o conjunto de bits altos em que os bytes vforam zero, pois os bits altos configurados devido a um valor maior que 0x80na primeira subexpressão são mascarados no segundo (27 de abril de 1987 por Alan Mycroft).
Agora podemos XOR o valor para testar ( x) com uma palavra que foi preenchida com o valor de bytes em que estamos interessados ( n). Como XORing um valor em si resulta em zero byte e diferente de zero, caso contrário, podemos passar o resultado para haszero.
Isso geralmente é usado em uma strchrimplementação típica .
(Stephen M Bennet sugeriu isso em 13 de dezembro de 2009. Mais detalhes no conhecido Bit Twiddling Hacks ).
PS
esse código está quebrado para qualquer combinação de 1111s ao lado de um0
O hack passa no teste de força bruta (apenas seja paciente):
#include <iostream>
#include <limits>
bool haszero(std::uint32_t v)
{
return (v - std::uint32_t(0x01010101)) & ~v & std::uint32_t(0x80808080);
}
bool hasvalue(std::uint32_t x, unsigned char n)
{
return haszero(x ^ (~std::uint32_t(0) / 255 * n));
}
bool hasvalue_slow(std::uint32_t x, unsigned char n)
{
for (unsigned i(0); i < 32; i += 8)
if (((x >> i) & 0xFF) == n)
return true;
return false;
}
int main()
{
const std::uint64_t stop(std::numeric_limits<std::uint32_t>::max());
for (unsigned c(0); c < 256; ++c)
{
std::cout << "Testing " << c << std::endl;
for (std::uint64_t w(0); w != stop; ++w)
{
if (w && w % 100000000 == 0)
std::cout << w * 100 / stop << "%\r" << std::flush;
const bool h(hasvalue(w, c));
const bool hs(hasvalue_slow(w, c));
if (h != hs)
std::cerr << "hasvalue(" << w << ',' << c << ") is " << h << '\n';
}
}
return 0;
}
Muitos votos positivos para uma resposta que torna a suposição um caractere = um byte, que hoje em dia não é mais o padrão
Obrigado pela observação.
A resposta era para ser apenas um ensaio sobre codificações de vários bytes / largura variável :-) (com toda a justiça, essa não é a minha área de especialização e não tenho certeza de que é o que o OP estava procurando).
De qualquer forma, parece-me que as idéias / truques acima poderiam ser adaptados ao MBE (especialmente codificações auto-sincronizáveis ):
- como observado no comentário de Johan, o hack pode 'facilmente' ser estendido para trabalhar com bytes duplos ou qualquer coisa (é claro que você não pode esticá-lo demais);
- uma função típica que localiza um caractere em uma cadeia de caracteres multibyte:
- contém chamadas para
strchr/ strstr(por exemplo, GNUlib coreutils mbschr )
- espera que eles estejam bem sintonizados.
- a técnica sentinela pode ser usada com um pouco de previsão.