"...there is always a well-known solution to every human problem — neat, plausible, and wrong.” - H.L. Mencken
In Computer Science we can rephrase:
"For every computing problem there exists a solution that is simple, elegant and wrong."
Problems on sites like hackerrank, in job interviews, pathfinding for actors in a game, drawing a fleet of spaceships in 3d, and indeed in any production system that involves sifting through data, is never about whether there is a solution.
The real question is always this:
"Find a way to reduce the order of complexity of this seemingly trivial task."
Counting from a
to b
while anding the values together is a linear time algorithm - O(b-a). That's fine when a and b are close. But this problem tells you that they are allowed to have an interval of up to 2^32-1, which is in the order of 4 billion tests.
As it happens, this problem can be reduced to O(log2(b-a)) because we know that b is bigger than a.
Look at the top bit of the following binary representations:
a 01001 (9)
b 11001 (25)
There are some common bits so intuitively we imagine that these bits are candidates for remaining 1s in the answer.
However, b has a bit that is a 1 whos value is an order of magnitude bigger than the top bit of a.
In order to count from a to b, every bit lower than this top bit will have to exist in every permutation of 1s and 0s - because this is how binary representations work.
If we permute a binary bitfield through every permutation, then eventually every bit in that field will at some point contain a 0. This means that the result of ANDing together every permutation of a bitfield is zero.
So the moment we find a 1 bit in b that is not in a, we can simply mask off all bits of lower magnitude from a.
Now the problem becomes:
Find the highest bit in b that does not exist in a and mask off all lower order bits from a. Return the result.
We've just reduced our search space from 0 < N < 2^32) to 0 < N <= 32. In other words, the complexity can be reduced from O(N) to O(log2(N)).
Here's a naiive solution - it doesn't even bother to optimise the computation of bitmasks - which passes all tests on hackerrank.
#define TESTING 0
#include <iostream>
#include <string>
#if TESTING
#include <sstream>
#endif
#include <cstdint>
using namespace std::literals;
#if TESTING
const char test_data[] =
R"__(
3
12 15
2 3
8 13
)__";
#endif
bool has_bit(const std::uint32_t x, int bitnum)
{
return (x & (1 << bitnum)) != 0;
}
constexpr std::uint32_t mask_here_down(int bitnum)
{
std::uint32_t result = 0;
while (bitnum--)
{
result |= std::uint32_t(1) << bitnum;
}
return result;
}
void algo(std::istream& in, std::ostream& out)
{
std::uint32_t a,b;
in >> a >> b;
for (int bit = 32 ; bit-- ; )
{
if (has_bit(b, bit) and not has_bit(a, bit))
{
std::cout << (a & ~mask_here_down(bit)) << std::endl;
break;
}
}
}
void run_tests(std::istream& in, std::ostream& out)
{
int n;
in >> n;
while (n--)
{
algo(in, out);
}
}
int main()
{
#if TESTING
auto in = std::istringstream(test_data);
#else
auto& in = std::cin;
#endif
run_tests(in, std::cout);
}