Зрозуміло, що найгірший випадок - O(N)
це дуже приємні мікрооптимізації.
Наївний метод виконує порівняння символів та порівняння кінця тексту для кожного персонажа.
Використання дозорного (тобто копії цільового символу в кінці тексту) зменшує кількість порівнянь до 1 на символ.
На рівні біт-подвійного змісту є:
#define haszero(v) ( ((v) - 0x01010101UL) & ~(v) & 0x80808080UL )
#define hasvalue(x, n) ( haszero((x) ^ (~0UL / 255 * (n))) )
знати, чи має байт у слові ( x
) певне значення ( n
).
Підвираз v - 0x01010101UL
, оцінюється на високий набір бітів у будь-якому байті, коли відповідний байт у v
нулі або більше, ніж 0x80
.
Підвираз ~v & 0x80808080UL
вираховує високі біти, встановлені в байтах, де байт v
не має свого високого біта (тому байт був меншим 0x80
).
За допомогою ANDing цих двох підвиразів ( haszero
) результат - це високі біти, встановлені там, де байти в v
нулі, оскільки високі біти, встановлені через значення, більше, ніж 0x80
у першому підвиразі, маскуються другим (27 квітня, 1987 р. Алан Мікрофт).
Тепер ми можемо XOR значення тестувати ( x
) за допомогою слова, яке було заповнене значенням байта, в якому нас цікавить ( n
). Оскільки XORing значення з самим собою призводить до нульового байта і ненульового значення, інакше ми можемо передати результат haszero
.
Це часто використовується в типовому strchr
виконанні.
(Стівен М Беннет запропонував це 13 грудня 2009 року. Докладніші відомості у відомих хай-бит-хайдінгах ).
PS
цей код порушений для будь-якої комбінації 1111
'поряд із a0
Хак проходить тест на грубу силу (просто будьте терплячі):
#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;
}
Багато відповідей на відповідь, що робить припущення одним символом = один байт, який на сьогоднішній день вже не є стандартом
Дякую за зауваження.
Відповідь мала на увазі будь-що, окрім есе про багатобайтові кодування з змінною шириною :-) (якщо чесно, це не моя область знань, і я не впевнений, що це шукало ОП).
Так чи інакше, мені здається, що вищезазначені ідеї / хитрощі можна було дещо адаптувати до MBE (особливо самосинхронізованих кодувань ):
- як зазначається в коментарі Йохана, хак можна «легко» розширити, щоб він працював на подвійні байти або що завгодно (звичайно, ви не можете занадто сильно розтягнути його);
- типова функція, яка розміщує символ у багатобайтовій рядку символів:
- містить виклики до
strchr
/ strstr
(наприклад, GNUlib coreutils mbschr )
- розраховує, що вони будуть добре налаштовані.
- техніку дозорного можна використовувати з невеликим передбаченням.