给定一个按 非递减顺序 排列的数字数组 digits
。你可以用任意次数 digits[i]
来写的数字。例如,如果 digits = ['1','3','5']
,我们可以写数字,如 '13'
, '551'
, 和 '1351315'
。
返回 可以生成的小于或等于给定整数 n
的正整数的个数 。
示例 1:
输入:digits = ["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:
输入:digits = ["1","4","9"], n = 1000000000 输出:29523 解释: 我们可以写 3 个一位数字,9 个两位数字,27 个三位数字, 81 个四位数字,243 个五位数字,729 个六位数字, 2187 个七位数字,6561 个八位数字和 19683 个九位数字。 总共,可以使用D中的数字写出 29523 个整数。
示例 3:
输入:digits = ["7"], n = 8 输出:1
提示:
1 <= digits.length <= 9
digits[i].length == 1
digits[i]
是从'1'
到'9'
的数digits
中的所有值都 不同digits
按 非递减顺序 排列1 <= n <= 10
9
解析:
我们要用这些数组中的数字组合成一个小于n的数字;
数位dp模板函数;
f(int i,int is_limit,bool is_num)
分别表示,第几个为,这个为是否有限制,这个为是否有前导0
class Solution {
public:
vector<string> digits;
int calc(int n){
string s = to_string(n);
int len = s.size();
int memo[len];
memset(memo,-1,sizeof(memo));
function<int(int,bool,bool)> f = [&](int i,bool is_limit,bool is_num) -> int{
if(i == len){
return is_num; //如果有数字合格
}
if(!is_limit && is_num && memo[i] != -1){ //这个数为没有限制且后面的数可以随机组合的
return memo[i];
}
int res = 0;//记录结果
if(!is_num){ //前导为0
res = f(i+1,false,false);
}
char up = is_limit ? s[i]: '9'; // 查看是否有没有限制
for(int d = 0; d < digits.size();d ++){
if(digits[d][0] > up){//有没有超出限制
break;
}
else{
res += f(i+1,is_limit&&digits[d][0] == up, true); // 注意还要判断是否个个为都是限制
//is_limit&&digits[d][0]== up 还有一个 这个为不为0 就是不是前导为0
}
}
if(!is_limit && is_num) memo[i] = res;//没有限制可以记录 记忆化搜索
return res;
};
return f(0,true,false);
}
int atMostNGivenDigitSet(vector<string>& digit, int n) {
digits = digit;
return calc(n);
}
};
如果一个正整数每一个数位都是 互不相同 的,我们称它是 特殊整数 。
给你一个 正 整数 n
,请你返回区间 [1, n]
之间特殊整数的数目。
示例 1:
输入:n = 20 输出:19 解释:1 到 20 之间所有整数除了 11 以外都是特殊整数。所以总共有 19 个特殊整数。
示例 2:
输入:n = 5 输出:5 解释:1 到 5 所有整数都是特殊整数。
示例 3:
输入:n = 135 输出:110 解释:从 1 到 135 总共有 110 个整数是特殊整数。 不特殊的部分数字为:22 ,114 和 131
提示:
1 <= n <= 2 * 109
解析:
每个数位互不相同的
可以用二进制快捷的判断是否有用过这个数字
一般的记忆化回溯数组只需要记录几个数字后面合法的数字个数,可是这里有个限制,所以要用二位的数组记录dp[i][mask] ,i表示第i为,mask表示那些数字已被使用过了,其他数字的组合个数。
class Solution {
public:
int countSpecialNumbers(int n) {
auto s = to_string(n);
int m = s.length(), dp[m][1 << 10];
memset(dp, -1, sizeof(dp));
function<int(int, int, bool, bool)> f = [&](int i, int mask, bool is_limit, bool is_num) -> int {
if (i == m) return is_num;
if (!is_limit && is_num && dp[i][mask] >= 0) return dp[i][mask];
int res = 0;
if (!is_num) res = f(i + 1, mask, false, false); // 可以跳过当前数位
for (int d = 1 - is_num, up = is_limit ? s[i] - '0' : 9; d <= up; ++d) // 枚举要填入的数字 d
if ((mask >> d & 1) == 0) // d 不在 mask 中
res += f(i + 1, mask | (1 << d), is_limit && d == up, true);
if (!is_limit && is_num) dp[i][mask] = res;
return res;
};
return f(0, 0, true, false);
}
};
时间复杂度:O(mD2^D),D为10