902. 最大为 N 的数字组合
我们有一组排序的数字 D
,它是 {'1','2','3','4','5','6','7','8','9'}
的非空子集。(请注意,'0'
不包括在内。)
现在,我们用这些数字进行组合写数字,想用多少次就用多少次。例如 D = {'1','3','5'}
,我们可以写出像 '13', '551', '1351315'
这样的数字。
返回可以用 D
中的数字写出的小于或等于 N
的正整数的数目。
示例 1:
输入:D = [“1”,”3”,”5”,”7”], N = 100
输出:20
解释:
可写出的 20 个数字是:
1, 3, 5, 7, 11, 13, 15, 17, 31, 33, 35, 37, 51, 53, 55, 57, 71, 73, 75, 77.
示例 2:
输入:D = [“1”,”4”,”9”], N = 1000000000
输出:29523
解释:
我们可以写 3 个一位数字,9 个两位数字,27 个三位数字,
81 个四位数字,243 个五位数字,729 个六位数字,
2187 个七位数字,6561 个八位数字和 19683 个九位数字。
总共,可以使用D中的数字写出 29523 个整数。
提示:
D
是按排序顺序的数字'1'-'9'
的子集。1 <= N <= 10^9
思路:
递推型写法:
- 先预处理
N
,抠出每一位十进制数,假设共有n
位 n - 1
位数一定都小于N
,每一位能选的个数有D.length
个,用循环处理一下- 从最高位向最低位依次考虑,只有对应位在D中存在才会继续向低位考虑。
class Solution {
public int atMostNGivenDigitSet(String[] digits, int v) {
String num = Integer.toString(v);
int n = digits.length, res = 0;
for (int i = 1; i < num.length(); i++) {
res += power(n, i);
}
boolean flag = true;
for (int i = 0; i < num.length(); i++) {
int x = num.charAt(i) - '0';
int cnt = power(n, num.length() - 1 - i);
int j = 0;
while (j < n && digits[j].charAt(0) - '0' < x) {
res += cnt;
j++;
}
if (j < n && digits[j].charAt(0) - '0' == x)
continue;
else {
flag = false;
break;
}
}
if (flag)
res++;
return res;
}
int power(int a, int b) {
int res = 1;
while (b > 0) {
if ((b & 1) == 1)
res *= a;
a *= a;
b >>= 1;
}
return res;
}
}
记忆化搜索:
模板:pos
表示当前枚举到第几位,从高位向低位枚举,假设最低位为第0位lim
表示高位的数是否贴合上界,1
表示贴合上界lead
表示高位是否全为0,1
表示高位全为0
class Solution {
List<Integer> list = new ArrayList<>();
List<Integer> num = new ArrayList<>();
int[] f = new int[10];
public int atMostNGivenDigitSet(String[] digits, int n) {
for (String s : digits) {
list.add(Integer.parseInt(s));
}
while (n > 0) {
num.add(n % 10);
n /= 10;
}
Arrays.fill(f, -1);
return dp(num.size() - 1, 1, 1);
}
int dp(int cur, int lim, int lead) {
if (cur == -1)
return lead == 0 ? 1 : 0;
int ans = f[cur];
if (lim != 1 && lead != 1 && ans != -1) return ans;
ans = 0;
int up = lim == 1 ? num.get(cur) : 9;
if (lead == 1) ans += dp(cur - 1, 0, 1);
for (int i : list) {
if (i > up) break;
if (lim == 1 && i == up)
ans += dp(cur - 1, 1, 0);
else ans += dp(cur - 1, 0, 0);
}
if (lim != 1 && lead != 1) f[cur] = ans;
return ans;
}
}