0

Given positive numbers N, K, D (1<= N <= 10^5, 1<=K<=N, 1<=D<=9). How many numbers with N digits are there, that have K consecutive digits D? Write the answer mod (10^9 + 7).

For example: N = 4, K = 3, D = 6, there are 18 numbers:

1666, 2666, 3666, 4666, 5666, 6660, 6661, 6662, 6663, 6664, 6665, 6666, 6667, 6668, 6669, 7666, 8666 and 9666.

Can we calculate the answer in O(N*K) (maybe dynamic programming)? I've tried using combination. If

N = 4, K = 3, D = 6. The number I have to find is abcd.

+) if (a = b = c = D), I choose digit for d. There are 10 ways (6660, 6661, 6662, 6663, 6664, 6665, 6666, 6667, 6668, 6669)

+) if (b = c = d = D), I choose digit for a (a > 0). There are 9 ways (1666, 2666, 3666, 4666, 5666, 6666, 7666, 8666, 9666) But in two cases, the number 6666 is counted twice. N and K is very large, how can I count all of them?

Izzy
  • 6,740
  • 7
  • 40
  • 84
just_dream
  • 23
  • 1
  • 4
  • What have you tried so far? Did you recieved this as your home work or some online programming forum? – SMA Oct 17 '14 at 09:24
  • This forum is not to resolve any programming question as a home work. Show us what you have tried so far or what algo you have in mind and we could help. – SMA Oct 17 '14 at 09:28
  • As I said, I think this is a Math problem. This is not my homework. If For length N and K same digits, I will count how many ways to make (N-K) digits. But my problem is some numbers are counted twice. – just_dream Oct 17 '14 at 09:49
  • In actuality, this problem is a good candidate for [math.stackexchange.com](http://math.stackexchange.com) or perhaps even [puzzling.stackexchange.com](http://puzzling.stackexchange.com). The language-agnostic nature is likely to lead people to solve it in very different and in not comparable ways. If you are truly curious about an optimal and correct solution, this is the thing the math people study more than programmers do. – HostileFork says dont trust SE Oct 29 '14 at 14:21

2 Answers2

1

Miquel is almost correct, but he missed a lot of cases. So, with N = 8, K = 5, and D = 6, we will need to look for those numbers that has the form:

66666xxx
y66666xx
xy66666x
xxy66666

with additional condition that y cannot be D.

So we can have our formula for this example:

66666xxx = 10^3
y66666xx = 8*10^2 // As 0 can also not be the first number
xy66666x = 9*9*10
xxy66666 = 9*10*9

So, the result is 3420.

For case N = 4, K = 3 and D = 6, we have

666x = 10
y666 = 8//Again, 0 is not counted!

So, we have 18 cases!

Note: We need to be careful that the first number cannot be 0! And we need to handle the case when D is zero too!

Update Java working code, Time complexity O(N-K)

 static long cal(int n, int k, int d) {
    long Mod = 1000000007;
    long result = 0;
    for (int i = 0; i <= n - k; i++) {//For all starting positions
        if (i != 0 || d != 0) {
            int left = n - k;
            int upper_half = i;//Amount of digit that preceding DDD
            int lower_half = n - k - i;//Amount of digit following DDD
            long tmp = 1;
            if (upper_half == 1) {
                if (d == 0) {
                    tmp = 9;
                } else {
                    tmp = 8;
                }
            }else if(upper_half >= 2){
                //The pattern is x..yDDD...
                tmp = (long) (9 * 9 * Math.pow(10, upper_half - 2));
            }

            tmp *= Math.pow(10, lower_half);
            //System.out.println(tmp + " " + upper_half + " " + lower_half);
            result += tmp;
            result %= Mod;

        }
    }
    return result;
}

Sample Tests:

  • N = 8, K = 5, D = 6

Output

3420
  • N = 4, K = 3, D = 6

Output

18
  • N = 4, K = 3, D = 0

Output

9
Pham Trung
  • 11,204
  • 2
  • 24
  • 43
  • In his answer, 6666 is a valid number that has 3 consecutive 6, but in his N = 8, K = 5, and D = 6, his answer matches yours. We have an inconsistency i how the problem is interpreted. – Miquel Oct 17 '14 at 10:22
  • @Miquel , the problem is correct :), I just make sure that I didn't count all number that have prefix DDD twice by adding y, you notice that I allow all digit after the prefix, so 6666 is still count in 666x, but not count in y666, will add another example for that :) – Pham Trung Oct 17 '14 at 10:31
  • I'm mulling over handling the leading zeros issue in my solution. Looking at this code, I was skeptical it handled them correctly; so put it into C++ (seems same, trivial changes) compared to a zero-savvy brute force method. [The resulting gist is here](https://gist.github.com/hostilefork/887afd1866c3d5288700). The answer matches on many, but also misses on many. The simplest cases to analyze are for instance `N = 1, K = 0, D = 6`. That should be 9 single digit numbers that aren't zero, instead of 18. Might offer a starting point for attacking the more convoluted cases; tough to step through! – HostileFork says dont trust SE Oct 17 '14 at 23:17
1

If one is looking for a mathematical solution (vs. necessarily an algorithmic one) it's good to look at it in terms of the base cases and some formulas. They might turn out to be something you can do some kind of refactoring and get a tidy formula for. So just for the heck of it...here's a take on it that doesn't deal with the special treatment of zeros. Because that throws some wrenches in.

Let's look at a couple of base cases, and call our answer F(N,K) (not considering D, as it isn't relevant to account for; but taking it as a parameter anyway.):

when N = 0

You'll never find any length sequences of digits when there's no digit.

  • F(0, K) = 0 for any K.

when N = 1

Fairly obvious. If you're looking for K sequential digits in a single digit, the options are limited. Looking for more than one? No dice.

  • F(1, K) = 0 for any K > 1

Looking for exactly one? Okay, there's one.

  • F(1, 1) = 1

Sequences of zero sequential digits allowed? Then all ten digits are fine.

  • F(1, 0) = 10

for N > 1

when K = 0

Basically, all N-digit numbers will qualify. So the number of possibilities meeting the bar is 10^N. (e.g. when N is 3 then 000, 001, 002, ... 999 for any D)

  • F(N, 0) = 10^N for any N > 1

when K = 1

Possibilities meeting the condition is any number with at least one D in it. How many N-digit numbers are there which contain at least one digit D? Well, it's going to be 10^N minus all the numbers that have no instances of the digit D. 10^N - 9^N

  • F(N, 1) = 10^N - 9^N for any N > 1

when N < K

No way to get K sequential digits if N is less than K

  • F(N, K) = 0 when N < K

when N = K

Only one possible way to get K sequential digits in N digits.

  • F(N, K) = 1 when N = K

when N > K

Okay, we already know that N > 1 and K > 1. So this is going to be the workhorse where we hope to use subexpressions for things we've already solved.

Let's start by considering popping off the digit at the head, leaving N-1 digits on the tail. If that N-1 series could achieve a series of K digits all by itself, then adding another digit will not change anything about that. That gives us a term 10 * F(N-1, K)

But if our head digit is a D, that is special. Our cases will be:

  1. It might be the missing key for a series that started with K-1 instances of D, creating a full range of K.

  2. It might complete a range of K-1 instances of D, but on a case that already had a K series of adjacent D (that we thus accounted for in the above term)

  3. It might not help at all.

So let's consider two separate categories of tail series: those that start with K-1 instances of D and those that do not. Let's say we have N=7 shown as D:DDDXYZ and with K=4. We subtract one from N and from K to get 6 and 3, and if we subtract them we get how many trailing any-digits (XYZ here) are allowed to vary. Our term for the union of (1) and (2) to add in is 10^((N-1)-(K-1)).

Now it's time for some subtraction for our double-counts. We haven't double counted any cases that didn't start with K-1 instances of D, so we keep our attention on that (DDDXYZ). If the value in the X slot is a D then it was definitely double counted. We can subtract out the term for that as 10^(((N - 1) - 1) - (K - 1)); in this case giving us all the pairings of YZ digits you can get with X as D. (100).

The last thing to get rid of are the cases where X is not a D, but in whatever that leftover after the X position there was still a K length series of D. Again we reuse our function, and subtract a term 9 * F(N - K, K, D).

Paste it all together and simplify a couple of terms you get:

  • F(N, K) = 10 * F(N-1,K,D) + 10^(N-K) - 10^(10,N-K-1) - 9 * F(N-K-1,K,D)

Now we have a nice functional definition suitable for Haskelling or whatever. But I'm still awkward with that, and it's easy enough to test in C++. So here it is (assuming availability of a long integer power function):

long F(int N, int K, int D) {
    if (N == 0) return 0;
    if (K > N) return 0;
    if (K == N) return 1;

    if (N == 1) {
        if (K == 0) return 10;
        if (K == 1) return 1;
        return 0;
    }
    if (K == 0)
        return power(10, N);
    if (K == 1)
        return power(10, N) - power(9, N);

    return (
        10 * F(N - 1, K, D)
        + power(10, N - K)
        - power(10, N - K - 1)
        - 9 * F(N - K - 1, K, D)
    );
}

To double-check this against an exhaustive generator, here's a little C++ test program that builds the list of vectors that it scans using std::search_n. It checks the slow way against the fast way for N and K. I ran it from 0 to 9 for each:

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

// http://stackoverflow.com/a/1505791/211160    
long power(int x, int p) {
    if (p == 0) return 1;
    if (p == 1) return x;

    long tmp = power(x, p/2);
    if (p%2 == 0) return tmp * tmp;
    else return x * tmp * tmp;
}

long F(int N, int K, int D) {
    if (N == 0) return 0;
    if (K > N) return 0;
    if (K == N) return 1;

    if (N == 1) {
        if (K == 0) return 10;
        if (K == 1) return 1;
        return 0;
    }
    if (K == 0) 
        return power(10, N);
    if (K == 1)
        return power(10, N) - power(9, N);

    return (
        10 * F(N - 1, K, D)
        + power(10, N - K)
        - power(10, N - K - 1) 
        - 9 * F(N - K - 1, K, D)
    );
}

long FSlowCore(int N, int K, int D, vector<int> & digits) {
    if (N == 0) {
        if (search_n(digits.begin(), digits.end(), K, D) != end(digits)) {
            return 1;
        } else
            return 0;
    }

    long total = 0;
    digits.push_back(0);
    for (int curDigit = 0; curDigit <= 9; curDigit++) {
            total += FSlowCore(N - 1, K, D, digits);
            digits.back()++;
    }
    digits.pop_back();
    return total;
}

long FSlow(int N, int K, int D) {
    vector<int> digits;
    return FSlowCore(N, K, D, digits);
}

bool TestF(int N, int K, int D) {
    long slow = FSlow(N, K, D);
    long fast = F(N, K, D); 
    cout << "when N = " << N
         << " and K = " << K 
         << " and D = " << D << ":\n";
    cout << "Fast way gives " << fast << "\n";
    cout << "Slow way gives " << slow << "\n";
    cout << "\n";
    return slow == fast;
}

int main() {
   for (int N = 0; N < 10; N++) {
        for (int K = 0; K < 10; K++) {
            if (!TestF(N, K, 6)) {
                exit(1);
            }
        }
    } 
}

Of course, since it counts leading zeros it will be different from the answers you got. See the test output here in this gist.

Modifying to account for the special-case zero handling is left as an exercise for the reader (as is modular arithmetic). Eliminating the zeros make it messier. Either way, this may be an avenue of attack for reducing the number of math operations even further with some transformations...perhaps.

  • Thanks for your answer. I read and understood it clearly :) To get the right answer, we just have to calculate F(N, K, D) - F(N-1, K, D). – just_dream Oct 17 '14 at 14:34
  • Update my code :), for recursive formula, I think you need more than N, K and D to determine correct parameters :) – Pham Trung Oct 17 '14 at 17:23
  • @just_dream Although this answer does not account for leading zeros, it solves what it defines. I do point out using test data for the other answer that [*it simply does not work*](https://gist.github.com/hostilefork/887afd1866c3d5288700). Explanation for accepting that one? If you've fixed it, you should write a new answer with a correct solution and accept that. – HostileFork says dont trust SE Oct 29 '14 at 13:32
  • @HostileFork I'm just confused about the answer. For the test n = 100000, k = 666, d = 6, the correct answer is 505047023. Does your program give the same? – just_dream Oct 29 '14 at 13:53
  • @just_dream This would count a zero, followed by 666 sixes, followed by a-million-minus-666-minus-one other digits. If that is not acceptable by your rules then it's counting things that shouldn't be counted and will be a higher number. The size of that test case has a runtime longer than I'll do with this method as-is, so I can't tell you what it will get...though the other method gets `290172669` very quickly. I never claimed my solution was ideal, I was just outlining a mathematical *strategy* of attack. Only suggesting not accepting a provably incorrect answer, but to investigate more. – HostileFork says dont trust SE Oct 29 '14 at 14:16
  • I'll note that when I say "higher number" I mean of course "before modular arithmetic is taken into account". It could very well be a smaller number after modulo. I did a quick variant which caches F for a given K and N, and threw in the modulo arithmetic. It quickly came back with 653642520, if anyone counting lead zeros wants to check. There's no feasible way to check it using brute force, though...which is why I stress the importance of making sure a solution holds up in tests over the smaller numbers you can brute force--and to use a math-based foundation so you can be confident in it. – HostileFork says dont trust SE Oct 30 '14 at 12:31