Start from integer 1
, remove any integer that contains 9
such as 9
, 19
, 29
...
Now, you will have a new integer sequence [1, 2, 3, 4, 5, 6, 7, 8, 10, 11, ...]
.
Given an integer n
, return the nth
(1-indexed) integer in the new sequence.
Example 1:
Input: n = 9 Output: 10
Example 2:
Input: n = 10 Output: 11
Constraints:
1 <= n <= 8 * 108
Related Topics:
Math
Intuition:
- Let
count(x)
be the count of positive numbers that are<= x
. Thiscount(x)
is monotonically non-decreasing forx
. - We can binary search in range
[1, INT_MAX]
, and find the first numberx
that satisfiescount(x) >= n
Algorithm:
Let cnt[i]
be the count of positive numbers that have exactly i
digits without any 9
s.
cnt[1] = 8 // 1-8
cnt[2] = 8 * 9 // 1-8 for the first digit, 0-8 for others
cnt[3] = 8 * 9 * 9
...
So, cnt[i] = 8 * 9^(i-1)
.
Let sum[i]
be the count of positive numbers that have <= i
digits without any 9
s.
So, sum[i] = SUM( cnt[j] | 1 <= j <= i )
.
For a given n
that has len
digits, we can use DFS to count the numbers that are <= n
. For dfs(i, len)
, we choose the digit to fill at the i
-th digit, say c
:
- If we fill a digit
d < c
, we havemul = c - (i == 0)
such options, then we can fill any non-negative numbers that havelen-i-1
digits without9
s. So this part contributesmul * (sum[len-i-1] + 1)
numbers. The+1
is for0
. - If
c != 9
and we fillc
, then we can fill any non-negative numbers that are<=
the lastlen-i-1
digits ofn
, and doesn't have9
s. So this part contributesc == 9 ? 0 : dfs(i + 1, len)
numbers.
// OJ: https://leetcode.com/problems/remove-9
// Author: github.com/lzl124631x
// Time: O(log(INT_MAX) * lg(INT_MAX))
// Space: O(lg(INT_MAX))
class Solution {
public:
int newInteger(int n) {
long long L = 1, R = INT_MAX;
auto count = [&](long long n) {
auto s = to_string(n);
int len = s.size();
vector<long long> sum(len + 1);
function<long long(int, int)> dfs = [&](int i, int len) {
if (i == len) return 1LL;
int c = s[i] - '0', mul = c - (i == 0);
return mul * (sum[len - i - 1] + 1) + (c == 9 ? 0 : dfs(i + 1, len));
};
long long cnt = 1;
for (int L = 1; L < len; ++L) {
cnt *= L == 1 ? 8 : 9; // 1-8 for the first digit, 0-8 for the other digits.
sum[L] = sum[L - 1] + cnt;
}
return sum[len - 1] + dfs(0, len);
};
while (L <= R) {
long long M = (L + R) / 2, cnt = count(M);
if (cnt < n) L = M + 1;
else R = M - 1;
}
return L;
}
};
Let's write the first numbers and try to notice a pattern. Those numbers are:
1, 2, 3, 4, 5, 6, 7, 8,
10, 11, 12, 13, 14, 15, 16, 17, 18,
20, 21, 22, 23, 24, 25, 26, 27, 28,
...
80, 81, 82, 83, 84, 85, 86, 87, 88,
100, 101, 102, ...
These numbers look exactly like all base-9 numbers!
Indeed, every base-9 number is a number in this sequence, and every number in this sequence is a base-9 number. Both this sequence and the sequence of all base-9 numbers are in increasing order. The answer is therefore just the n-th base-9 number.
// OJ: https://leetcode.com/problems/remove-9
// Author: github.com/lzl124631x
// Time: O(1)
// Space: O(1)
class Solution {
string base9(int n) {
string ans;
while (n) {
ans += '0' + n % 9;
n /= 9;
}
reverse(begin(ans), end(ans));
return ans;
}
public:
int newInteger(int n) {
return stoi(base9(n));
}
};