数位DP
给一个区间[A,B],求在A,B之间满足条件的数字的个数。解题思路,使用递归每位数字变化,求满足的个数。
模板1.0 不考虑下界
一、不考虑前导 0 的模板
以下是不考虑前导 0 的模板代码:
static int dfs(int pos, boolean limit, String str, int[] dp) {
if (pos == str.length()) {
return 1;
}
//没有限制本位填写的数字
if (dp[pos]!= -1 &&!limit) {
return dp[pos];
}
int res = 0;
int up = limit? str.charAt(pos) - '0' : 9;
for (int d = 0; d <= up; d++) {
res += dfs(pos + 1, limit && d == up, str, dp);
}
//没有限制本位填写的数字
if (!limit) {
dp[pos] = res;
}
return res;
}
二、考虑前导 0 的模板
考虑前导 0 的模板代码如下:
static int dfs(int pos, boolean limit, boolean isNum, String str, int[] dp) {
if (pos == str.length()) {
return isNum? 1 : 0;
}
if (dp[pos]!= -1 &&!limit) {
return dp[pos];
}
int res = 0;
//如果右前导0,当前位也可以继续填写0,即跳过
if (!isNum && str.charAt(pos) == '0') {
res += dfs(pos + 1, false, false, str, dp);
}
//往上一行不是return,会继续往下走,所以要判断isNum,前面填写了前导0,本位可以填写数字
int start = isNum? 0 : 1;
int up = limit? str.charAt(pos) - '0' : 9;
for (int d = start; d <= up; d++) {
res += dfs(pos + 1, limit && d == up, true, str, dp);
}
if (!limit ) {
dp[pos]= res;
}
return res;
}
模板2.0 考虑下界
一、不考虑前导0
二、考虑前导0
三、数位概念
数位是指把一个数字按照个、十、百、千等一位一位拆开,关注它每一位上的数字。
例如在十进制数中,每一位数字的取值范围是 0 - 9,其他进制数可类比十进制。比如二进制,每一位数字的取值范围就是 0 - 1。在 leetcode 中目前的题目大多是基于十进制和二进制的。
四、数位 DP(题目应用特征和条件)
- 计数:数位 DP 常用于计数问题。
- 输入限制:输入会提供一个数字区间(或者只提供上界)作为计数的限制条件。
- 转换理解:题目条件经过转换后可以使用「数位」的思想去判断和理解。
五、为什么可以用数位 DP
- 朴素计数的不足:最朴素的计数方法是从小到大依次加一。
- 重复计算问题:但对于位数比较多的数,这样的计数过程中有许多重复的部分。例如从 700 数到 799、从 800 数到 899、和从 900 数到 999 的过程非常相似,它们都是后两位从 00 变到 99,不一样的地方只有百位这一位。
- 避免重复计算的方法:我们可以把重复计算的过程归并保证不重复计算,即将这些过程中产生的计数答案存放在一个数组里。此数组根据题目具体要求设置状态,用递推或 DP 的方式进行状态转移。
- 统计答案的方式:统计答案建议使用记忆化搜索,为了统计所有不超过上限的答案,要从高到低枚举每一位,再考虑每一位都可以填哪些数字,最后利用通用答案数组统计答案。
六、解题方法
- 相对固定参数设置
- 假设 n 是范围的最大值。
- pos(必选):表示第 pos 个数位。
- islimit(必选):表示当前是否受到了 n 的约束。若为真,则第 i 位填入的数字至多为 s[i],否则至多为 9。例如 n = 234,如果前面填了 23,那么最后一位至多填 4;如果前面填的不是 23,那么最后一位至多填 9。如果在受到约束的情况下填了 s[i],那么后续填入的数字仍会受到 n 的约束。
- isnum(可选):表示 i 前面的数位是否填了数字。若为假,则当前位可以跳过(不填数字),或者要填入的数字至少为 1;若为真,则要填入的数字可以从 0 开始。例如 n = 123,在 i = 0 时跳过的话,相当于后面要构造的是一个 99 以内的数字了,如果 i = 1 不跳过,那么相当于构造一个 10 到 99 的两位数,如果 i = 1 跳过,相当于构造的是一个 9 以内的数字。
- mask(可选):表示前面选过的数字集合,换句话说,第 i 位要选的数字不能在 mask 中。
加油啦!加油鸭,冲鸭!!!

浙公网安备 33010602011771号