-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
Copy pathcount-numbers-with-non-decreasing-digits.py
108 lines (96 loc) · 3.51 KB
/
count-numbers-with-non-decreasing-digits.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# Time: O(n^2), n = len(r)
# Space: O(n)
# math, stars and bars, combinatorics
class Solution(object):
def countNumbers(self, l, r, b):
"""
:type l: str
:type r: str
:type b: int
:rtype: int
"""
MOD = 10**9+7
fact, inv, inv_fact = [[1]*2 for _ in xrange(3)]
def nCr(n, k):
while len(inv) <= n: # lazy initialization
fact.append(fact[-1]*len(inv) % MOD)
inv.append(inv[MOD%len(inv)]*(MOD-MOD//len(inv)) % MOD) # https://cp-algorithms.com/algebra/module-inverse.html
inv_fact.append(inv_fact[-1]*inv[-1] % MOD)
return (fact[n]*inv_fact[n-k] % MOD) * inv_fact[k] % MOD
def nHr(n, k):
return nCr(n+k-1, k)
def count(x):
digits_base = []
while x:
x, r = divmod(x, b)
digits_base.append(r)
digits_base.reverse()
if not digits_base:
digits_base.append(0)
result = 0
for i in xrange(len(digits_base)):
if i-1 >= 0 and digits_base[i-1] > digits_base[i]:
break
for j in xrange(digits_base[i-1] if i-1 >= 0 else 0, digits_base[i]):
result = (result + nHr((b-1)-j+1, len(digits_base)-(i+1))) % MOD
else:
result = (result+1)%MOD
return result
return (count(int(r)) - count(int(l)-1)) % MOD
# Time: O(n^2), n = len(r)
# Space: O(n)
# math, stars and bars, combinatorics
class Solution2(object):
def countNumbers(self, l, r, b):
"""
:type l: str
:type r: str
:type b: int
:rtype: int
"""
MOD = 10**9+7
fact, inv, inv_fact = [[1]*2 for _ in xrange(3)]
def nCr(n, k):
while len(inv) <= n: # lazy initialization
fact.append(fact[-1]*len(inv) % MOD)
inv.append(inv[MOD%len(inv)]*(MOD-MOD//len(inv)) % MOD) # https://cp-algorithms.com/algebra/module-inverse.html
inv_fact.append(inv_fact[-1]*inv[-1] % MOD)
return (fact[n]*inv_fact[n-k] % MOD) * inv_fact[k] % MOD
def nHr(n, k):
return nCr(n+k-1, k)
def decrease(digits):
for i in reversed(xrange(len(digits))):
if digits[i]:
digits[i] -= 1
break
digits[i] = 9
def divide(digits, base):
result = []
r = 0
for d in digits:
q, r = divmod(r*10+d, base)
if result or q:
result.append(q)
return result, r
def to_base(digits, base):
result = []
while digits:
digits, r = divide(digits, base)
result.append(r)
result.reverse()
return result
def count(digits):
digits_base = to_base(digits, b)
result = 0
for i in xrange(len(digits_base)):
if i-1 >= 0 and digits_base[i-1] > digits_base[i]:
break
for j in xrange(digits_base[i-1] if i-1 >= 0 else 0, digits_base[i]):
result = (result + nHr((b-1)-j+1, len(digits_base)-(i+1))) % MOD
else:
result = (result+1)%MOD
return result
digits_l = map(int, l)
decrease(digits_l)
digits_r = map(int, r)
return (count(digits_r) - count(digits_l)) % MOD