Problem Description
There is a hidden 30‐bit number n (i.e. 0 ≤ n ≤ 2^30 – 1). You have access to an API commonBits(num) that works in two steps. First it calculates and returns the number of bit positions (among the 30 least–significant bits) where n and num have the same bit (i.e. both 0 or both 1). Then it updates n by replacing it with n XOR num. Although n changes with every call, your task is to determine the original (initial) value of n using as many queries as you like (provided your queries remain in range).
Key Insights
- The API returns 30 minus the Hamming distance between n and the query number.
- Every query permanently “flips” the bits of n at the positions where the query has a 1 bit.
- Querying with 0 does not change n yet returns info (namely the count of zeros in n) since only positions where n has 0 will match.
- By carefully performing queries and “undoing” the cumulative XOR effects, it is possible to “peel‐off” bit information one by one.
- A strategy is to intentionally use “calibration” queries (using 0) to know the baseline number of 0 bits of the current state, and then make a query with a power of 2. The difference in the response tells you whether that specific bit in the current state was 0 or 1.
- Finally, because every query flips n, the overall effect is that after a fixed sequence of queries the final value equals (original_n XOR cumulative_query_mask). Knowing the cumulative mask lets us recover the original n.
Space and Time Complexity
Time Complexity: O(30) = O(1) per test case (we make ~60 queries at most)
Space Complexity: O(1)
Solution
The idea is to “probe” each bit from the most significant bit (bit 29) down to the least significant one. We first make a query with 0 to determine the baseline number of positions where n has a 0; call this baseline. Then, for each bit position i, we:
- Query with 0 to “read” the current state’s zero–count (this query does not change n).
- Query with (1 << i) and observe the response. In the current state there are two possibilities:
- If the i–th bit is 1, flipping it (by XOR with 1<<i) will turn it to 0 so that the response increases by 1 relative to the baseline (because that position now “mismatches” the 0 in the query would have contributed a 1 if it were already 1).
- If the i–th bit is 0, flipping it makes it 1 causing one fewer matched bit (a decrease by 1).
- Based on the comparison between the two responses (before and after the flip) we determine the bit value in the current state.
- Although updating n with each query changes the internal state, note that the overall effect of a sequence of queries Q is that the final state equals original_n XOR Q. As we record the “flip” (i.e. the power–of–2 mask we used for each bit) in a cumulative variable, we can “undo” the overall effect at the very end.
- Finally, after processing all 30 bits, we perform one final query with 0 to read the final state and then recover the original n by XOR–ing out the cumulative query mask.
We use simple bit–manipulation and careful simulation of the state evolution. The main “gotcha” is that every query (except with 0) flips some bits; therefore we must keep track of the cumulative XOR (the aggregate of all non–zero queries), so that once we know the final state, we can recover the original n.
Data structures used: a couple of integer variables to record the cumulative mask and temporary results. The algorithmic approach is greedy bit by bit determination using differences in the API’s returned “common bits” count.
Code Solutions
Below are line–by–line commented solutions in Python, JavaScript, C++ and Java. (The interactive function commonBits(num) is assumed to be provided by the judge.)