Longest Increasing Numeral sub-sequence of array - algorithm

We got this problem in our course that no one who I had talked to solved it. I would like for some help. So here's the problem:
Let A be array of length n which contains n digits (digit is between 0-9).
A numeral sub-sequence of A is a sequence of positive numbers which their digits compose a sub-sequence of A, when all digits of a certain number in the sequence appear in a row in A.
For example: the sequence 13,1,345,89,23 is a numeral sub-sequence of input array A:
[1,3,5,1,2,3,4,5,8,9,4,5,2,3]
Length of a numeral sub-sequence is the amount of numbers which appear in it (in the example above: 5)
Numeral sub-sequence is increasing if every number in the sequence is bigger than the number before it.
The request is to find an algorithm in dynamic programming approach (based on recursive formula) that finds the longest increasing numeral sub-sequence of an input array A.
Thanks in advance for all helpers!

Look at the first digit in the array. Either this digit is not part of a number in your number sequence or it is. If it is, the number could have 1, 2, ..., n digits. For each guess, return:
not in a number: return f(array[2...n], -1)
1st digit of 1-digit number: return array[1] union f(array[2...n], number(array[1]))
1st digit of 2-digit number: return array[1...2] union f(array[3...n], number(array[1...2]))
1st digit of 3-digit number: return array[1...3] union f(array[4...n], number(array[1...3]))
...
1st digit of n-digit number: return array[1...n]
There are some optimizations you can do here to skip some steps along the way.
f(array[1...k], x) = f(array[1...k], y) if the smallest choice for the next number in the sequence given hypothetical last numbers x and y is the same. So, if the smallest choice for the next number in array[1...k] is the same for x and y, and we already computed the value of f for x, we can reuse that value for y.
f(array[1...k], x) = c + f(array[2...k], x) whenever array[1] = 0, where c = 1 if x < 0 and c = 0 if x >= 0. That is, we can ignore leading zeroes except possibly a leading zero at the beginning of the array which should always be chosen as our first one-digit number.
when deciding whether a digit will be the first digit of a k-digit number, if you never choose leading zeroes, you know an upper bound on the number of remaining numbers in your sequence is given by n/k, since any numbers chosen after this one will need to be at least k digits long. If you remember the longest sequence you've seen so far, you can recognize paths that have no hope of doing better than what you've seen and ignore them.
if an array has at least k(k+1)/2 non-zero digits in it, there is a number sequence of length at least k obtained by taking numbers with 1, 2, ..., k non-zero digits sequentially left to right. So, if you pre-compute this value, you can potentially avoid some paths right off the bat.
Here's rough pseudocode with the optimizations discussed:
solve(array[1...n])
z = number of non-zero entries in array
last_number = -1
min_soln = floor((sqrt(1 + 8z) - 1) / 2)
return solve_internal(array[1...n], min_soln, last_number)
memo = {}
solve_internal(array[1...n], min_soln, last_number)
// ignore potentially leading zeroes except the first one
if array[1] = 0 then
if last_number < 0 then
return {0} union solve_internal(array[2...n], min_soln - 1, 0)
else then
return solve_internal(array[2...n], min_soln, last_number)
// abort since we don't have enough digits left to get a solution
if floor(n / #digits(last_number)) < min_soln return []
// look up current situation in previous partial solutions
z = smallest number formable in array greater than last_number
if memo contains (n, z) then
return memo[n, z]
soln = {}
for k = 1 to n do
soln_k = solve_internal(array[k+1...n], min_soln - 1, array[1...k])
if |soln_k| > |soln| then
soln = soln_k
min_soln = |soln|
memo[n, z] = soln
return soln

Related

How to get the intuition behind the solution?

I was solving the below problem from USACO training. I found this really fast solution for which, I am finding it unable to absorb fully.
Problem: Consider an ordered set S of strings of N (1 <= N <= 31) bits. Bits, of course, are either 0 or 1.
This set of strings is interesting because it is ordered and contains all possible strings of length N that have L (1 <= L <= N) or fewer bits that are `1'.
Your task is to read a number I (1 <= I <= sizeof(S)) from the input and print the Ith element of the ordered set for N bits with no more than L bits that are `1'.
sample input: 5 3 19
output: 10110
The two solutions I could think of:
Firstly the brute force solution which goes through all possible combinations of bits, selects and stores the strings whose count of '1's are less than equal to 'L' and returning the Ith string.
Secondly, we can find all the permutations of '1's from 5 positions with range of count(0 to L), sort the strings in increasing order and returning the Ith string.
The best Solution:
The OP who posted the solution has used combination instead of permutation. According to him, the total number of string possible is 5C0 + 5C1 + 5C2 + 5C3.
So at every position i of the string, we decide whether to include the ith bit in our output or not, based on the total number of ways we have to build the rest of the string. Below is a dry run of the entire approach for the above input.
N = 5, L = 3, I = 19
00000
at i = 0, for the rem string, we have 4C0 + 4C1 + 4C2 + 4C3 = 15
It says that, there are 15 other numbers possible with the last 4 positions. as 15 is less than 19, our first bit has to be set.
N = 5, L = 2, I = 4
10000
at i = 1, we have 3C0 + 3C1 + 3C2 (as we have used 1 from L) = 7
as 7 is greater than 4, we cannot set this bit.
N = 5, L = 2, I = 4
10000
at i = 2 we have 2C0 + 2C2 = 2
as 2 <= I(4), we take this bit in our output.
N = 5, L = 1, I = 2
10100
at i = 3, we have 1C0 + 1C1 = 2
as 2 <= I(2) we can take this bit in our output.
as L == 0, we stop and 10110 is our answer. I was amazed to find this solution. However, I am finding it difficult to get the intuition behind this solution.
How does this solution sort-of zero in directly to the Ith number in the set?
Why does the order of the bits not matter in the combinations of set bits?
Suppose we have precomputed the number of strings of length n with k or fewer bits set. Call that S(n, k).
Now suppose we want the i'th string (in lexicographic order) of length N with L or fewer bits set.
All the strings with the most significant bit zero come before those with the most significant bit 1. There's S(N-1, L) strings with the most significant bit zero, and S(N-1, L-1) strings with the most significant bit 1. So if we want the i'th string, if i<=S(N-1, L), then it must have the top bit zero and the remainder must be the i'th string of length N-1 with at most L bits set, and otherwise it must have the top bit one, and the remainder must be the (i-S(N-1, L))'th string of length N-1 with at most L-1 bits set.
All that remains to code is to precompute S(n, k), and to handle the base cases.
You can figure out a combinatorial solution to S(n, k) as your friend did, but it's more practical to use a recurrence relation: S(n, k) = S(n-1, k) + S(n-1, k-1), and S(0, k) = S(n, 0) = 1.
Here's code that does all that, and as an example prints out all 8-bit numbers with 3 or fewer bits set, in lexicographic order. If i is out of range, then it raises an IndexError exception, although in your question you assume i is always in range, so perhaps that's not necessary.
S = [[1] * 32 for _ in range(32)]
for n in range(1, 32):
for k in range(1, 32):
S[n][k] = S[n-1][k] + S[n-1][k-1]
def ith_string(n, k, i):
if n == 0:
if i != 1:
raise IndexError
return ''
elif i <= S[n-1][k]:
return "0" + ith_string(n-1, k, i)
elif k == 0:
raise IndexError
else:
return "1" + ith_string(n-1, k-1, i - S[n-1][k])
print([ith_string(8, 3, i) for i in range(1, 94)])

calculate combinations of numbers where a specific digit occurs more than n number of times

how do we calculate combinations of numbers where a specific digit occurs more than n number of times ?
eg : in 4 digit number 1112, 1 occurs 3 times. again 1211, 1121, 2111, 1111 would be such numbers. basically 1 more than 2 times(3,4 times) what would be the formula for calculating such permutations
Let's say f(n, k) is the count of n digit numbers where a specific digit is repeated k times, for k <= n.
We can start by considering all n-k digit numbers that don't have the specified digit, then all ways of inserting that digit.
There are 9^(n-k) numbers with n-k digits that don't have the specified digit. This includes numbers with leading zeroes (comment if these are disallowed).
Now, for each of these, in how many ways can we insert k copies of the specified digit?
The answer is n! / (k! * (n-k)!), which is the number of permutations of n things, divided by the number of permutations of the non-specified digit (which we don't want to permute among themselves) and the number of permutations of the specified digit (ditto).
So f(n,k) = 9^(n-k) * n! / (k! * (n-k)!)
Now, what you asked for is the count of n digit numbers where a specific digit is repeated k OR MORE times, for k <= n. Let's call this g(n,k).
g(n,k) = sum(f(n,i)) for i in {k, k+1, ..., n}
Sample Code (Ruby): (fact is factorial)
def f(n,k)
return 9**(n-k)*fact(n) / (fact(k)*fact(n-k))
end
def g(n,k)
ans = 0
k.upto(n) do |i|
ans += f(n,i)
end
return ans
end
Sample output:
g(5,0) = 100,000 (as expected, all possible 5 digit numbers = 10**5)
g(5,1) = 40,951 (as expected, 10**5 - 9**5)
g(5,2) = 8,146
g(5,3) = 856
g(5,4) = 46
g(5,5) = 1

The number prodigy : reverse sum of pattern

Number prodigy is given X - there's a X digit number N, reverse of N is M. Number prodigy is interested in finding out how many X digit numbers are of form : N+M=10^X-1 and N is expected not have trailing zeroes. Means that N%10 != 0 .
In case of X=1, 9 such combinations exist.
Denote A[i] - the i'th digit of A.
We first need to understand that to get N+M=10^X-1, we need N[i]+M[i]=9 for all i. Since M[i]=N[X-i], it means we need N[i] + N[X-i] = 9. This means, once N[i] is set, also N[X-i].
We can now derive a recursive formula:
F(X) = 10*F(X-2)
The idea is - we look at the first digit of X, we have 10 possibilities for it, and for each possibility, we set N[0] and N[X-1].
However, this allows leading and trailing zeros, which we don't want. The first and last number can be anything by 0.
G(X) = 8*F(X-2)
The above is chosing one of 1,2,...,8 as N[0], then setting (one option) the last number so N[X-1] = 9 - N[0], and invoke the recursive call without restrictions. Note neither N[0] nor N[X-1] can be zero.
Base clauses:
F(0) = 1
F(1) = 0
F(1)=0 because there is no natural number n such that n+n=9.
All in all, we found a recursive formula to compute the total number of elements. This recursive formula can be transformed into a close one with some basic algebra. I leave this part for you.

Count of numbers between A and B (inclusive) that have sum of digits equal to S

The problems is to find the count of numbers between A and B (inclusive) that have sum of digits equal to S.
Also print the smallest such number between A and B (inclusive).
Input:
Single line consisting of A,B,S.
Output:
Two lines.
In first line the number of integers between A and B having sum of digits equal to S.
In second line the smallest such number between A and B.
Constraints:
1 <= A <= B < 10^15
1 <= S <= 135
Source: Hacker Earth
My solution works for only 30 pc of their inputs. What could be the best possible solution to this?
The algorithm I am using now computes the sum of the smallest digit and then upon every change of the tens digit computes the sum again.
Below is the solution in Python:
def sum(n):
if (n<10):return n
return n%10 + sum(n/10)
stri = raw_input()
min = 99999
stri = stri.split(" ")
a= long (stri[0])
b= long (stri[1])
s= long (stri[2])
count= 0
su = sum(a)
while a<=b :
if (a % 10 == 0 ):
su = sum(a)
print a
if ( s == su):
count+=1
if (a<= min):
min=a
a+=1
su+=1
print count
print min
There are two separate problems here: finding the smallest number between those numbers that has the right digit sum and finding the number of values in the range with that digit sum. I'll talk about those problems separately.
Counting values between A and B with digit sum S.
The general approach for solving this problem will be the following:
Compute the number of values less than or equal to A - 1 with digit sum S.
Compute the number of values less than or equal to B with digit sum S.
Subtract the first number from the second.
To do this, we should be able to use a dynamic programming approach. We're going to try to answer queries of the following form:
How many D-digit numbers are there, whose first digit is k, whose digits that sum up to S?
We'll create a table N[D, k, S] to hold these values. We know that D is going to be at most 16 and that S is going to be at most 136, so this table will have only 10 × 16 × 136 = 21,760 entries, which isn't too bad. To fill it in, we can use the following base cases:
N[1, S, S] = 1 for 0 ≤ S ≤ 9, since there's only one one-digit number that sums up to any value less than ten.
N[1, k, S] = 0 for 0 ≤ S ≤ 9 if k ≠ S, since no one-digit number whose first digit isn't a particular sum sums up to some value.
N[1, k, S] = 0 for 10 ≤ S ≤ 135, since no one-digit number sums up to exactly S for any k greater than a single digit.
N[1, k, S] = 0 for any S < 0.
Then, we can use the following logic to fill in the other table entries:
N[D + 1, k, S] = sum(i from 0 to 9) N[D, i, S - k].
This says that the number of (D+1)-digit numbers whose first digit is k that sum up to S is given by the number of D-digit numbers that sum up to S - k. The number of D-digit numbers that sum up to S - k is given by the number of D-digit numbers that sum up to S - k whose first digit is 0, 1, 2, ..., 9, so we have to sum up over them.
Filling in this DP table takes time only O(1), and in fact you could conceivably precompute it and hardcode it into the program if you were really concerned about time.
So how can we use this table? Well, suppose we want to know how many numbers that sum up to S are less than or equal to some number X. To do this, we can process the digits of X one at a time. Let's write X one digit at a time as d1 ... dn. We can start off by looking at N[n, d1, S]. This gives us the number of n-digit numbers whose first digit is d1 that sum up to S. This may overestimate the number of values less than or equal to X that sum up to S. For example, if our number is 21,111 and we want the number of values that sum up to exactly 12, then looking up this table value will give us false positives for numbers like 29,100 that start with a 2 and are five digits long, but which are still greater than X. To handle this, we can move to the next digit of the number X. Since the first digit was a 2, the rest of the digits in the number must sum up to 10. Moreover, since the next digit of X (21,111) is a 1, we can now subtract from our total the number of 4-digit numbers starting with 2, 3, 4, 5, ..., 9 that add up to 10. We can then repeat this process one digit at a time.
More generally, our algorithm will be as follows. Let X be our number and S the target sum. Write X = d1d2...dn and compute the following:
# Begin by starting with all numbers whose first digit is less than d[1].
# Those count as well.
result = 0
for i from 0 to d[1]:
result += N[n, i, S]
# Now, exclude everything whose first digit is d[1] that is too large.
S -= d[1]
for i = 2 to n:
for j = d[i] to 8:
result -= N[n, d[i], S]
S -= d[i]
The value of result will then be the number of values less than or equal to X that sum up to exactly S. This algorithm will only run for at most 16 iterations, so it should be very quick. Moreover, using this algorithm and the earlier subtraction trick, we can use it to compute how many values between A and B sum up to exactly S.
Finding the smallest value in [A, B] with digit sum S.
We can use a similar trick with our DP table to find the smallest number greater than A number that sums up to exactly S. I'll leave the details as an exercise, but as a hint, work one digit at a time, trying to find the smallest number for which the DP table returns a nonzero value.
Hope this helps!

Sum of last k digits same as sum of first k digits

I want to find if sum of first k digits of few numbers in given range is equal to sum of last k digits. Here the range is very large and k is less than 20.
One way we can do this is by brute force method. Can someone suggest some other efficient algo. for same?
If it is a range, the first digits will not change often and the last digits will change in a simple way. S is the sum of the first 20 digits. While the secund digit doesn't change, the sum will be increased by one when you go to the next digit. So if all yours digits, except the last one, are fixed, and if the sum with the last digit equal to i is Si, you the only good last digit is n= S - Si + i. You then have to check if n is between 0 and 9, and if the resulting number is in the interval. This decrease by ten the number of lookups.
You can check for the next secund lower digits.
If the first n is lower than 0, you need to decrease the secund digit by -n. Call n2 this secund digit. If n2 > = 0, the good numbers will end by (n2,0), (n2 -1,1), ..., (0, n2). This decrease the complexity by 100.
If n is bigger than 10, you increase the second digit by n-9. Call n2 the second digit. If n2<=9, the good numbers are (n2,9),(n2-1,8),...,(0,something).
This also decrease the complexity by 100.
You can do the same for the third digit, and then for the fourth, up to the 20. This will result in just 1 sum, and a complexity in O(number of solutions), so it is minimal. For coding, be careful that your firsts numbers can change. Do one computation per group of 20 first numbers.
one theoretical improvement to the brute force method:
1) sum up the frist k digits, store in sumFirst
2) sum up the last k digits, but stop if sum exceeds sumFirst.
Point 2 could save summing up some of the last few digits.
But you have to measure if the additional logic, costs more then simply adding all k digits.
Optimization N-k
One way to improve the algorithm is if when the number having N digits has the following property:N < 2k.
For instance if N = 5 and k = 3, 5 < 2x3, digits being
abcde
you only have to count ab against de (ie no need to check k (3) digits, since the 3rd is shared by k-last and k-first digits).In other words, the number of digits to be counted both sides is only
min(k, N-k), having N >= k
If you are going to use that multiple times for the same array, you can sum all element with previous elements which is O(n) where the size of array is n i.e
for(int i = 1; i < n; i++)
arr[i] = arr[i] + arr[i-1];
This will convert your array from probability density function to cumulative distribution function (for discrete numbers). Therefore your query is going to be O(1) i.e.
if(arr[k-1] == (arr[n-1]-arr[n-k])) //arr[k-1] is sum of first k element
return true;
return false;
another improvement over the brute force:
i = 0, T = 0
while |T| < 9 * (k - i)
T = T + last[i] - first[i]
i = i + 1
return T == 0

Resources