Hackerrank has a problem called Decibinary numbers which are essentially numbers with 0-9 digit values but are exponentiated using powers of 2. The question asks us to display the xth decibinary number. There is another twist to the problem. Multiple decibinary numbers can equal the same decimal number. For example, 4 in decimal can be 100, 20, 12, and 4 in decibinary.
At first, I thought that finding how many decibinary numbers for a given decimal number would be helpful. I consulted this post for a bit help ( https://math.stackexchange.com/questions/3540243/whats-the-number-of-decibinary-numbers-that-evaluate-to-given-decimal-number ). The post was a bit too hard to understand but then I also realized that even though we have how many decibinary numbers a decimal number can have, this doesn't help FINDING them (at least to my knowledge) which is the original goal of the question.
I do realize that for any decimal number, the largest decibinary number for it will simply be its binary representation. For ex, for 4 it is 100. So the brute force approach would be to check all numbers in this range for each decimal number and see if their decibinary representation evaluates to the given decimal number, but it is clearly evident that this approach will never pass since the input constraints define x to be from 1 to 10^16. Not only that, we have to find the xth decibinary number for a q amount of queries where q is from 1 to 10^5.
This question falls under the section of dp but I am confused how dp will be used or how it is even possible. In order for calculating the xth decibinary number q times (which is described in the brute force method above) it would be better to use a table (like the problem suggests). But for that, we would need to store and calculate 10^16 integers since that is the how big x can be. Assuming an integer is 4 Bytes, 4B * 10^16 ~= 4B * (2^3)^16 = 2^50 Bytes.
Can someone please explain how this problem is solved optimally. I am still new to CP so if I have made an error in something, please let me know.
(see link below for full problem statement): https://www.hackerrank.com/challenges/decibinary-numbers/problem
This is solvable with about 80 MB of data. I won't give code, but I will explain the strategy.
Build a lookup
count[n][i]
that gives you the number of ways to get the decimal numbern
using the firsti
digits. You start by inserting0
everywhere, and then put a1
incount[0][0]
. Now start filling in using the rule:It turns out that you only need the first 19 digits, and you only need counts of
n
up to2**19-1
. And the counts all fit in 8 bytelong
s.Once you have that, create a second data structure
count_below[n]
which is the count of how many decibinary numbers will give a value less thann
. Use the same range ofn
as before.And now a lookup proceeds as follows. First you do a binary search on
count_below
to find the last value that has less than your target number below it. Subtractingcount_below
from your query, you know which decibinary number of that value you want.Next, search through
count[n][i]
to find thei
such that you get your target query withi
digits, and not with less. This will be the position of the leading digit of your answer. You then subtract offcount[n][i-1]
from your query (all the decibinaries with fewer digits). Then subtract offcount[n-2**i][i-1]
,count[n-2* 2**i][i-1]
, ...count[n-8*2**i][i-1]
until you find what that leading digit is. Now you subtract the contribution of that digit from the value, and repeat the logic for finding the correct decibinary for that smaller value with fewer digits.Here is a worked example to clarify. First the data structures for the first 3 digits and up to
2**3 - 1
:Let's find the 20th.
count_below[6]
is 14 andcount_below[7]
is 20 so our decimal sum is6
.20 - count_below[6] = 6
th decibinary with decimal sum 6.count[6][2]
is 4 whilecount[6][3]
is 6 so we have a non-zero third digit.count[6][3] - count[6][2] = 2
with a non-zero third digit.count[1][6 - 2**2]
is 2, so 2 have 3rd digit 1.2
.count[2][1]
is 1 andcount[2][2]
is 2 so it has a non-zero second digit.count[2][2] - count[2][1] = 1
st with a non-zero second digit.2 - 2**1 = 0
.And thus you find that the answer is 110.
Now for such a small number, this was a lot of work. But even for your hardest lookup you'll only need about 20 steps of a binary search to find your decimal sum, another 20 steps to find the position of the first non-zero digit, and for each of of those digits, you'll have to do 1-9 different calculations to find what that digit is. Which means only hundreds of calculations to find the number.