The OP's code is clean and quite readable.
Kudos for its presentation (seriously!).
The function's name, purpose and its variables are unmistakeable, and newer compiler capabilities ("binary constants") have been used.
"Efficiency" seems to be the primary focus behind this question. Careful examination of the operations suggests there might be a "better way"...
"Table Lookup" of, in this case, 10 bit patterns (210 = 1024 elements) would be very fast. That technique could rapidly gobble through all the array's bytes of data. The downside to a LUT would be tedious and error-prone manually populating such a single purpose table, OR writing code to generate the 1024 values. The latter approach would entail solving the coding problem for 1024 values, then capturing its output for conversion to a compile-time table. The unknown "use case" would have to justify the work. (And, would be a solution to only one specific search pattern.) Additionally, this is tractable for 8+2=10 bits. It doesn't scale well, though, once the target pattern goes over 10 bits to 12, 18 or ?? bits...
Proving ground
This reviewer admits to difficulty "tracking" the values of too many variables, especially when differences with both constants and changing variable values are stirred into the mix. I prefer a simpler approach...
Below, I've taken the OP's code, lightly changed only a few places in order to use my older compiler, and added two things: 1) A simple "test harness" that deals with a tiny array of only 2 bytes and 2) a revision of the OP's code that still loops in search of the target pattern. (Written as C, but should be good-to-go as C++, I hope.)
Unfortunately, this has shown that there is something not-quite-right in the OP's code. The comparison of the two approaches reveals a discrepancy when the two array values combine to form
0b0000 0001 0100 0000 (320 = 256 + 64),
and the discrepancy obviously continues for more array values higher than that.
Here is the full code, presented "as is" for the OP to study and consider. In brief, it uses a shifting "window" to isolate continuous regions of a 10bit register (sliding left-to-right) and counting matches of the target pattern. (I've not been sitting in an "interview situation" when writing this. NO(!) denigration of the OP or that code is to be inferred from this post, please!) I'd be happy to respond to any questions or comments about this post.
#include <stdio.h>
#include <stdint.h>
int count_pattern_occurrences(const uint8_t* array, size_t size, uint8_t pattern) {
pattern = 0x5;
if (size == 0 || pattern > 0x7) {
return 0; // Array is empty or invalid pattern
}
int count = 0;
uint32_t value = 0; // To hold the accumulated bits
int bit_count = 0;
for (size_t i = 0; i < size; ++i) {
value = (value << 8) | array[i];
bit_count += 8;
// Check within the current byte
for (int bit_pos = bit_count - 8; bit_pos <= bit_count - 3; ++bit_pos) {
if (((value >> (bit_count - 3 - bit_pos)) & 0x7) == pattern) {
++count;
}
}
// Prepare for the next iteration, keeping only the last 2 bits
value &= 0x3;
bit_count = 2;
}
return count;
}
int occurs_101( uint8_t arr[], size_t size ) {
int cnt = 0;
uint32_t bits = 0;
for( size_t i = 0; i < size; ++i ) {
bits = ((bits & 0x3) << 8) | arr[i];
uint32_t pattern = 0x5 << 8;
uint32_t msk = 0x7 << 8;
do {
pattern >>= 1;
msk >>= 1;
cnt += (bits & msk) == pattern;
} while( !(msk & 1) );
}
return cnt;
}
int main( void ) {
uint8_t arr[2] = { 0 };
do {
do {
int v0 = count_pattern_occurrences( arr, 2, 0x5 );
int v1 = occurs_101( arr, 2 );
if( v0 != v1 ) {
printf( "%5d: %d %d\n", ((int)arr[0]<<8)+arr[1], v0, v1 );
getchar();
}
} while( ++arr[1] != 0 );
} while( ++arr[0] != 0 );
puts( "All done!" );
return 0;
}
Beyond this, it becomes apparent that searching through large arrays might benefit from gathering-up several bytes into a uint64_t or even a uint128_t, as has been suggested in another answer, then using some shifting and XOR operations to process many bits simultaneously, followed by some form of 'bit count' on the resulting value. Again, the "use case" would need to be defined to decide how much time and effort should be invested in the problem.
In conclusion, the OP's code is clean and, with some pencil work, could be checked for its functionality. The highest priority has to be the correctness of the code. (And, I admit that I've not checked my own version for all 65536 possible combinations of 2x 8bits of input. This I leave for the OP, or you, the gentle reader, to perform.)
Worthwhile?
Revisiting code that's been left to simmer for a few hours is a good way to shape it into what might be considered "a keeper." Here's a slightly more compact version of the code above:
int occurs_101a( uint8_t arr[], size_t size ) {
int cnt = 0;
uint32_t bits = 0;
for( size_t i = 0; i < size; ++i ) {
bits = ((bits << 8) | arr[i] ) & 0x3FF;
for( uint32_t wrk = bits; wrk >= 0x5; wrk >>= 1 )
cnt += (wrk & 0x7) == 0x5;
}
return cnt;
}
It doesn't matter at which end of any 10bit sample register the tabulation begins, so the previous "mask" does not need to be manipulated (shifted). The above also has the possibility of "early determination of contiguous high-order clear bits" that would preclude some fruitless looping.
Worthwhile?
Overboard?
int occurs_101b( uint8_t arr[], size_t size ) {
int cnt = 0;
uint32_t b = 0, w; // bits register and working register
for( size_t i = 0; i < size; ++i ) {
b = ((b << 8) | arr[i]) & 0x3FF;
w = ((((b >> 1) & ~b) >> 1) & b) & 0xFF; // 0b101
for( ; w; cnt++ ) w &= w - 1; // Brian K's algo
}
return cnt;
}
The above has an inner loop to include Brian Kernighan's well known "bit count" algorithm. This version is now amenable, with some tweaking, to using wider registers to combine several array bytes for simultaneous 'consolidation' and counting of instances of the target pattern. Note, however, that the target bit pattern is now expressed in bitwise operations making it slightly more difficult to discern.
Always have some fun with whatever you do.
Interview (and career) tip
Here's another version to ponder:
int occurs_101c( uint8_t arr[], size_t size ) {
int cnt = 0;
uint32_t b = 0; // bits register
for( size_t i = 0; i < size; ++i ) {
b = ((b << 8) | arr[i] ) & 0x3FF;
cnt += ((b>>0 & 7) == 5) // 0000000101
+ ((b>>1 & 7) == 5) // 0000001010
+ ((b>>2 & 7) == 5) // 0000010100
+ ((b>>3 & 7) == 5) // 0000101000
+ ((b>>4 & 7) == 5) // 0001010000
+ ((b>>5 & 7) == 5) // 0010100000
+ ((b>>6 & 7) == 5) // 0101000000
+ ((b>>7 & 7) == 5);// 1010000000
}
return cnt;
}
The above contains quite a few operations, but is "branchless" (apart from the required outer loop). Further, it can be easily "reasoned about" by anyone with a modicum of programming experience. In other words, it is simple to write, simple to read, and its correctness can be verified visually. Although not efficient or fast, its results will always be correct (for the problem as stated.) It's a good starting point! The worst thing a coder can produce is code that works most of the time.
Most of the answers, here, refer to the speed of using a LUT. One of those provides a function, containing some intricate but working code, that populates a LUT suitable for its implementation of the solution. The simple and verifiable function above could be called 1024 times (210) with 1024 bit patterns by a caller that, likewise, populates an appropriate LUT (or outputs text suitable for copy/paste into a compile-time array.)
Consider the hypothetical that there is very limited time to write code to solve a problem. Now there are two priorities: Correctness (always), and a Deadline. If the interviewer got nothing better than the code above, the interviewee can highlight its simplicity and its correctness. Incorrect or incomplete solutions both score very low marks. If judged to be "inefficient. Do over.", the code above can be used to populate that efficient LUT with correct values. Or, as demonstrated by the offered "test harness", this simple version can be used to verify the results of a "more efficient" version that uses bit manipulation only.
Aside: Storing 1024 values that vary from 0 to 5, each in a byte, can be improved upon.
Exercise: cut that memory footprint in half by storing (and retrieving) 4bit values from an array of 512 bytes (each being a pair of "nibbles".) How does this affect performance? Can it be written "branchless"?
Recommend not walking away from this problem because it seems to be solved. It is by exploring alternatives that one adds-to or strengthens one's intuition and storehouse of "on-hand" solutions.
7as "the number of days in a week" just in case a "week" ever needed to be redefined as something other than7days. KISS principles dictate "worry about core requirements, not some hypothetical." \$\endgroup\$0b1x1(wherex== "don't care"), or0b1xx1, or any other of the innumerable variations. "Sufficient unto the day is the evil thereof". \$\endgroup\$