Count Special Integers
Count Special Integers
We call a positive integer special if all of its digits are distinct.
Given a positive integer , return the number of special integers that belong to the interval .
Example 1:
Input: n = 20 Output: 19 Explanation: All the integers from 1 to 20, except 11, are special. Thus, there are 19 special integers.
Example 2:
Input: n = 5 Output: 5 Explanation: All the integers from 1 to 5 are special.
Example 3:
Input: n = 135 Output: 110 Explanation: There are 110 integers from 1 to 135 that are special. Some of the integers that are not special are: 22, 114, and 131.
Constraints:
解题思路
经典数位dp题目。先用动态规划预处理求长度为,且首位数字(从左至右)为的没有重复数字出现的数的个数。为了避免重复选择了数字,还需要多开一维来记录选择了哪些数字,用二进制状态来表示。
因此状态表示为,表示长度为,选择了的数字的状态为,且首位的数字为的数的个数。根据下一位数字的不同来进行集合的划分,状态转移方程为
然后进行数位dp,将的每一位取出来,表示的第位数,最高位为第位。其中不可以是,即不能含有前导的数。
最后再求首位为的数,枚举长度为的数进行累加,为的位数。
AC代码如下:
1 class Solution { 2 public: 3 int countSpecialNumbers(int n) { 4 vector<int> num; 5 while (n) { // 把n的每一位取出来 6 num.push_back(n % 10); 7 n /= 10; 8 } 9 n = num.size(); 10 11 int f[11][1 << 10][10] = {0}; 12 vector<int> mp[11]; // mp[i]包含位数为i且没有重复数字的数所表示的状态 13 for (int i = 0; i < 1 << 10; i++) { 14 int cnt = 0; 15 for (int j = 0; j < 10; j++) { 16 cnt += i >> j & 1; 17 } 18 mp[cnt].push_back(i); 19 } 20 21 for (int i = 0; i <= 9; i++) { // 处理长度为1的边界情况 22 f[1][1 << i][i] = 1; 23 } 24 for (int i = 2; i <= n; i++) { 25 for (int &j : mp[i]) { 26 for (int k = 0; k <= 9; k++) { 27 if (j >> k & 1) { // 状态j的第k位要为1,表示包含数字k 28 for (int u = 0; u <= 9; u++) { 29 // 状态j的第u位要为1,表示包含数字u,且不能有重复数字,即u!=k 30 if (u != k && j >> u & 1) f[i][j][k] += f[i - 1][j - (1 << k)][u]; 31 } 32 } 33 } 34 } 35 } 36 37 int ret = 0, last = 0; // last是一个状态,表示之前的位都选择了哪些数字 38 for (int i = n - 1; i >= 0; i--) { 39 for (int j = i == n - 1; j < num[i]; j++) { // 首位不能为0 40 for (int &k : mp[i + 1]) { 41 if (!(last & k)) ret += f[i + 1][k][j]; // 后面的位选择的数字不能包含之前选过的数字 42 } 43 } 44 45 if (last >> num[i] & 1) break; // num[i]之前已经选过,break 46 last |= 1 << num[i]; // 第i位选择了数字num[i] 47 48 if (i == 0) ret++; // 选完了所有位的数,再判断一下n是否满足所有位都不同 49 } 50 51 // 统计位数小于n的位数的剩下所有数 52 for (int i = 1; i < n; i++) { 53 for (int j = 1; j <= 9; j++) { 54 for (int &k : mp[i]) { 55 ret += f[i][k][j]; 56 } 57 } 58 } 59 60 return ret; 61 } 62 };
再介绍一种数理统计的方法。
假设有位,位数小于的数一定小于,因此先统计有多少个位数小于的数满足每一位都不一样,假设一个位数(),那么满足条件的数有(这里),其中首位不能选,因此有种选择。当只有一位数那么就有种情况。
然后再统计位数为的数有多少个是满足条件的。
AC代码如下:
1 class Solution { 2 public: 3 int countSpecialNumbers(int n) { 4 vector<int> num; 5 while (n) { 6 num.push_back(n % 10); 7 n /= 10; 8 } 9 10 int ret = 0; 11 for (int i = 1; i < num.size(); i++) { // 统计位数小于num.size()的个数 12 int t = 9; 13 for (int j = 1, k = 9; j < i; j++, k--) { 14 t *= k; 15 } 16 ret += t; 17 } 18 19 int last = 0; 20 reverse(num.begin(), num.end()); 21 for (int i = 0; i < num.size(); i++) { // 统计位数为num.size()的个数 22 for (int j = !i; j < num[i]; j++) { 23 if (last >> j & 1) continue; 24 int t = 1; 25 for (int k = 0, u = 9 - i; k < num.size() - i - 1; k++, u--) { 26 t *= u; 27 } 28 ret += t; 29 } 30 31 if (last >> num[i] & 1) break; 32 last |= 1 << num[i]; 33 34 if (i == num.size() - 1) ret++; 35 } 36 37 return ret; 38 } 39 };
参考资料
y总,比赛遇到原题了怎么办?力扣第306场周赛:https://www.bilibili.com/video/BV1wv4y1c71n?spm_id_from=333.337.search-card.all.click
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/16589698.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效