Nó được hiểu rằng trường hợp xấu nhất là O(N)
, có một số tối ưu hóa vi mô rất tốt đẹp.
Phương pháp ngây thơ thực hiện so sánh ký tự và so sánh cuối văn bản cho mỗi ký tự.
Sử dụng một sentinel (tức là một bản sao của ký tự đích ở cuối văn bản) làm giảm số lượng so sánh xuống 1 trên mỗi ký tự.
Ở cấp độ twiddling có:
#define haszero(v) ( ((v) - 0x01010101UL) & ~(v) & 0x80808080UL )
#define hasvalue(x, n) ( haszero((x) ^ (~0UL / 255 * (n))) )
để biết nếu bất kỳ byte nào trong một từ ( x
) có một giá trị cụ thể ( n
).
Biểu thức con v - 0x01010101UL
, ước tính thành một bit cao được đặt trong bất kỳ byte nào mỗi khi byte tương ứng v
bằng 0 hoặc lớn hơn 0x80
.
Biểu thức con ~v & 0x80808080UL
ước tính các bit cao được đặt theo byte trong đó byte của v
không có tập bit cao (do đó byte nhỏ hơn 0x80
).
Bằng cách ANDing hai biểu thức con ( haszero
) này, kết quả là các bit cao được đặt trong đó các byte v
bằng 0, vì các bit cao được đặt do giá trị lớn hơn 0x80
trong biểu thức phụ đầu tiên bị che đi bởi lần thứ hai (27 tháng 4, 1987 bởi Alan Mycroft).
Bây giờ chúng ta có thể XOR giá trị để kiểm tra ( x
) bằng một từ đã được điền với giá trị byte mà chúng ta quan tâm ( n
). Bởi vì XORing một giá trị với chính nó dẫn đến một byte bằng 0 và khác không, chúng ta có thể chuyển kết quả đến haszero
.
Điều này thường được sử dụng trong một strchr
thực hiện điển hình .
(Stephen M Bennet đã đề xuất điều này vào ngày 13 tháng 12 năm 2009. Thông tin chi tiết khác trong Hacker Twiddling nổi tiếng ).
PS
mã này bị hỏng cho bất kỳ sự kết hợp nào 1111
bên cạnh một0
Bản hack vượt qua bài kiểm tra sức mạnh vũ phu (chỉ cần kiên nhẫn):
#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;
}
Rất nhiều sự ủng hộ cho một câu trả lời làm cho giả định một chararacter = một byte, ngày nay không còn là tiêu chuẩn nữa
Cảm ơn bạn đã nhận xét.
Câu trả lời có nghĩa là bất cứ điều gì ngoại trừ một bài tiểu luận về mã hóa nhiều byte / độ rộng biến đổi :-) (trong tất cả các công bằng không phải là lĩnh vực chuyên môn của tôi và tôi không chắc đó là những gì OP đang tìm kiếm).
Dù sao, đối với tôi, các ý tưởng / thủ thuật trên có thể phần nào được điều chỉnh theo MBE (đặc biệt là mã hóa tự đồng bộ hóa ):
- như đã lưu ý trong nhận xét của Johan, hack có thể 'dễ dàng' được mở rộng để hoạt động với hai byte hoặc bất cứ thứ gì (tất nhiên bạn không thể kéo dài nó quá nhiều);
- một chức năng điển hình định vị một ký tự trong chuỗi ký tự đa nhân:
- kỹ thuật sentinel có thể được sử dụng với một chút tầm nhìn xa.