最大为N的数字组合
给定一个按 非递减顺序 排列的数字数组 digits 。你可以用任意次数 digits[i] 来写的数字。
例如,如果 digits = ['1','3','5'],我们可以写数字,如 '13', '551', 和 '1351315'。
返回 可以生成的小于或等于给定整数 n 的正整数的个数
1. 数位dp
class Solution {
public:
int atMostNGivenDigitSet(vector<string>& digits, int n) {
auto s = to_string(n); //转字符串逐位递归分析
int m = s.length(), memo[m];
memset(memo,-1,sizeof(memo));
int notall = 0;
function<int(int, int, bool)> f = [&](int i,bool prezero,bool is_limit) -> int {//视情况记录已遍历值的某种特征,如1的个数,mask状态,
if (i == m) return !prezero;
if (!is_limit&&memo[i]!= -1) //已经存储过,直接剪枝返回
return memo[i];
int res = 0; //计算1出现的个数
if(prezero) notall += f(i+1,prezero,0);//单独处理前导0,同时不受限,含前导零的数需要单独计算
int up = is_limit ? s[i] - '0' : 9; // 根据受限与否决定枚举数上界
//做选择
for (int d = 0; d < digits.size(); d++){ //遍历可选数
int cur = digits[d][0]-'0';
if(cur>up) break;//剪枝
res += f(i + 1, 0, is_limit && cur == up);//移动到下一位,判断限制情况
}
if (!is_limit) //记录非限制数
memo[i] = res;
return res;
};
return f(0,true,true)+notall; //从下标0开始,刚开始受限
}
};