如果一个正整数每一个数位都是 互不相同 的,我们称它是 特殊整数 。
给你一个 正 整数 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的问题,如果很久不做这类问题的话也是很容易翻车的。
一般数位DP的问题都会给一个限制条件,如何求在固定范围内满足该限制条件的数有多少个。比如本题的限制条件就是每个数位上的数各不相同。我们要求的有两部分:以j开头的i位数满足限制条件的有多少个;不超过指定的数满足限制条件的数有多少个。
第一个问题我们往往用f[i][j]表示以j为末尾的i位数满足条件的数的数量,但是对于霸体而已,以什么数开头并不重要,重要的是几位数。我们只需要指定i位数中满足条件的有多少个就可以了。
所以很明显,可以用一个数组预处理出满足条件的i位数有多少个,用q[i]表示i位数中满足条件的数的个数。
第二个问题,不超过指定的数满足限制条件的数有多少个?
比如n = 987,首先所有的两位数和1位数中满足条件的均小于n,也就是q[1] + q[2]个。然后就考察3位数的情况。最高位是1 - 8时,不管后两位是多少,均小于n,所以一共8 * 9 * 8个数。
然后考察首位是9的情况,第二位是0 - 7时,不管第三位是多少,均小于n,而前两位占了两个数,第三位只能有8种选择了,一共有8 * 8个数。
前两位是98时,最后一位是0-6均满足条件,同时987也没有重复数字,也是满足条件的,一共有8个满足条件的数。
所以从这个例子可以看出,我们需要自高位向低位遍历n,每个位置上小于n对应位置上的数,只需要取考虑后面位数的数字,如果对应位置上的数等于n对应位置上的数,就需要继续考察下一位。
class Solution {
public:
int f[12][12];
int countSpecialNumbers(int n) {
if(n < 10) return n;
vector<int> num;
while(n) num.push_back(n % 10),n /= 10;
int res = 0;
vector<bool> m(12,false);
bool flag = true;
vector<int> q(12,0);
q[1] = 9;
int r = 9;
for(int i = 2;i <= 10;i++) {
q[i] = q[i-1] * r;
r--;
}
for(int i = 1;i < num.size();i++) res += q[i];//位数低于n的情况
int choose = 0;
for(int i = num.size() - 1;i >= 0;i--){
int x = num[i];
choose++;//已经选择了的位数
int s = 1;//剩下位置上的符合条件的数
for(int j = 0,k = 10 - choose;j < i;j++,k--) {
s *= k;
}
for(int j = (choose == 1);j < x;j++) {
if(!m[j]) res += s;
}
if(m[x]) break;//出现重复数,枚举终止
m[x] = true;
if(!i) res++;//枚举到最后一位
}
return res;
}
};
虽然本题分类讨论的逻辑以及代码都不复杂,但数位DP问题需要注意的地方很多。比如本题单独讨论了1到n - 1位数中满足条件的数的数量,但是很多问题中会将这个归类于讨论第一位数时,首位为0的情况,但是本题由于后面的数与之前出现的数相关,所以无法合并。举个例子,比如前两位是10后面枚举时就只有2 - 9这么多数可以枚举了,但是如果首位是0,代表没有数字,后面依旧可以枚举0的,比如020,也就是20,是合法的数。
如果严格按照DP的思想来分析本题,可以用g[i][j]表示前i - 1位数与n相同,第i位数为j,且j小于n的第i位数的符合条件的i位数有多少个。正如开头所说,设n的位数是k,位数小于k的合法的数的数量是很容易求出来的,我们进行状态划分只需要考虑前i位与n相同的k位数情况。
总的来说,数位DP的问题需要先将数逐位分离存下来,然后求以j位末尾满足条件的i位数的数量;然后遍历n位数,依次讨论前面的数与n相同,当前位置的数比n对应位置小的情况,需要特判的是首位为0的情况以及遍历结束时恰好n也是满足条件的数的情况。