Reputation: 179
Some numbers are called unlucky if their decimal representation contain the number 5
.
For example: 123456
, 245
, 1555
are unlucky numbers and 111
, 123
, 147
are not unlucky numbers.
How many unlucky numbers are there in segment [a:b]
?, where 1 < a < b
.
For example a = 1
, b = 17
, we have the result is 2
. Because, there are only 5
, 15
.
This is my try: I call
f(a)
is the amount of unlucky numbers in segment[1;a]
, then we havef(a+1)
is the the amount of unlucky numbers in segment[1;a+1]
, so we have:f(a+1)=f(a)
ifa+1
is not unlucky number andf(a+1)=f(a)+1
if a+1 is unlucky number. My problem is to computef(a)
withO(log(n))
, but I can't come to good solution!
Upvotes: 3
Views: 111
Reputation: 2777
This can be easilly solved in O(log N) time using dynamic programming known as "dp on digits"
First note that instead of calculating unlucky numbers in inertval [L,R]
we can instead define some function
f(X)
that calculates unlucky numbers in interval [0,x]
, so unlucky numbers in interval [L,R]
can be calculated as f(R) - f(L-1)
Creating function f()
is kinda straightforward, we have our state as dp[pos][ls][unlucky]
where:
pos
- current digit we are trying to fillls
- just a flag that saysif our prefix matches the limit of how high we can go unlucky
- another flag that says if our number is already unlucky or notThen we can do simple recursion expansion digit by digits + memoization.
Sample code (C++11) :
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll dp[51][2][2];
ll digits[51];
int amount_of_digits;
ll solve(ll pos, ll ls, ll unlucky)
{
if(pos >= amount_of_digits)return unlucky;
if(dp[pos][ls][unlucky]!=-1)return dp[pos][ls][unlucky];
dp[pos][ls][unlucky]=0;
for(int i=0; i<=(ls ? 9 : digits[pos]); i++) //next digit
dp[pos][ls][unlucky]+=solve(pos+1,ls | (i<digits[pos]), unlucky | (i==5));
return dp[pos][ls][unlucky];
}
ll f(ll x)
{
string s = to_string(x);
amount_of_digits = s.size();
for(int i=0; i<s.size(); i++)
digits[i] = s[i]-'0';
memset(dp, -1, sizeof dp);
return solve(0,0,0);
}
ll calc_interval(ll L, ll R)
{
return f(R) - f(L-1);
}
int main()
{
cout<<calc_interval(2,17);
}
Upvotes: 2
Reputation: 5676
I won't give you the full solution, but I'll give you a hint that should point you in the correct direction. You need to apply some mathematics (permutation & combination) knowledge to this problem.
Suppose you want to find the number of unlucky numbers in [0, 12345). With some math, you can calculate the number of such numbers in [0, 10000) in O(1). Then, you could calculate the number of such numbers in [10000, 12000) in O(1). Then do it for [12000, 12300). Now if you see where we're going...
To find the number of unlucky numbers in [a, b], we can simply calculate the number of unlucky numbers in [0, a) and the number of unlucky numbers in [0, b+1).
Note: You don't actually need to calculate the number of unlucky numbers in [0, 10000) in O(1). It also suffices to find a way to calculate the number of unlucky numbers in [0, 10000) from the number of unlucky numbers in [10000, 12000) in O(1).
Upvotes: 3