--- comments: true difficulty: 困难 edit_url: https://github.com/doocs/leetcode/edit/main/solution/2300-2399/2376.Count%20Special%20Integers/README.md rating: 2120 source: 第 306 场周赛 Q4 tags: - 数学 - 动态规划 --- # [2376. 统计特殊整数](https://leetcode.cn/problems/count-special-integers) [English Version](/solution/2300-2399/2376.Count%20Special%20Integers/README_EN.md) ## 题目描述

如果一个正整数每一个数位都是 互不相同 的,我们称它是 特殊整数

给你一个  整数 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 。

 

提示:

## 解法 ### 方法一:数位 DP 定义 $m$ 表示数字 $n$ 的位数。我们可以将数字分成两类:(1) 数字位数小于 $m$;(2) 数字位数等于 $m$。 对于第一类,我们可以枚举数字的位数 $i$,其中 $i∈[1,m)$,第一位的数字不为 $0$,有 $[1,9]$ 可选,共 $9$ 种可能。剩余需要选择 $i-1$ 位数字,可选数字为 $[0,9]$ 的数字中除去第一位,共 $9$ 种可能。因此,第一类的数字共有: $$ \sum \limits_{i=1}^{m-1} 9\times A_{9}^{i-1} $$ 对于第二类,数字的位数等于 $m$,我们从 $n$ 的高位(即 $i=m-1$)开始处理。不妨设 $n$ 当前位的数字为 $v$。 如果当前是 $n$ 的最高一位,那么数字不能为 $0$,可选数字为 $[1,v)$,否则可选数字为 $[0,v)$。若当前可选数字 $j$,那么剩余低位可选的数字总共有 $A_{10-(m-i)}^{i}$,累加到答案中。 以上我们算的是可选数字小于 $v$ 的情况,若等于 $v$,则需要继续外层循环,继续处理下一位。如果数字 $n$ 所有位均不重复,则 $n$ 本身也是一个特殊整数,需要累加到答案中。 时间复杂度 $O(m^2)$,其中 $m$ 是数字 $n$ 的位数,这里我们假定 $A_{m}^{n}$ 可以 $O(1)$ 时间算出。 相似题目: - [233. 数字 1 的个数](https://github.com/doocs/leetcode/blob/main/solution/0200-0299/0233.Number%20of%20Digit%20One/README.md) - [357. 统计各位数字都不同的数字个数](https://github.com/doocs/leetcode/blob/main/solution/0300-0399/0357.Count%20Numbers%20with%20Unique%20Digits/README.md) - [600. 不含连续 1 的非负整数](https://github.com/doocs/leetcode/blob/main/solution/0600-0699/0600.Non-negative%20Integers%20without%20Consecutive%20Ones/README.md) - [788. 旋转数字](https://github.com/doocs/leetcode/blob/main/solution/0700-0799/0788.Rotated%20Digits/README.md) - [902. 最大为 N 的数字组合](https://github.com/doocs/leetcode/blob/main/solution/0900-0999/0902.Numbers%20At%20Most%20N%20Given%20Digit%20Set/README.md) - [1012. 至少有 1 位重复的数字](https://github.com/doocs/leetcode/blob/main/solution/1000-1099/1012.Numbers%20With%20Repeated%20Digits/README.md) #### Python3 ```python class Solution: def countSpecialNumbers(self, n: int) -> int: def A(m, n): return 1 if n == 0 else A(m, n - 1) * (m - n + 1) vis = [False] * 10 ans = 0 digits = [int(c) for c in str(n)[::-1]] m = len(digits) for i in range(1, m): ans += 9 * A(9, i - 1) for i in range(m - 1, -1, -1): v = digits[i] j = 1 if i == m - 1 else 0 while j < v: if not vis[j]: ans += A(10 - (m - i), i) j += 1 if vis[v]: break vis[v] = True if i == 0: ans += 1 return ans ``` #### Java ```java class Solution { public int countSpecialNumbers(int n) { List digits = new ArrayList<>(); while (n != 0) { digits.add(n % 10); n /= 10; } int m = digits.size(); int ans = 0; for (int i = 1; i < m; ++i) { ans += 9 * A(9, i - 1); } boolean[] vis = new boolean[10]; for (int i = m - 1; i >= 0; --i) { int v = digits.get(i); for (int j = i == m - 1 ? 1 : 0; j < v; ++j) { if (vis[j]) { continue; } ans += A(10 - (m - i), i); } if (vis[v]) { break; } vis[v] = true; if (i == 0) { ++ans; } } return ans; } private int A(int m, int n) { return n == 0 ? 1 : A(m, n - 1) * (m - n + 1); } } ``` #### C++ ```cpp class Solution { public: int countSpecialNumbers(int n) { int ans = 0; vector digits; while (n) { digits.push_back(n % 10); n /= 10; } int m = digits.size(); vector vis(10); for (int i = 1; i < m; ++i) { ans += 9 * A(9, i - 1); } for (int i = m - 1; ~i; --i) { int v = digits[i]; for (int j = i == m - 1 ? 1 : 0; j < v; ++j) { if (!vis[j]) { ans += A(10 - (m - i), i); } } if (vis[v]) { break; } vis[v] = true; if (i == 0) { ++ans; } } return ans; } int A(int m, int n) { return n == 0 ? 1 : A(m, n - 1) * (m - n + 1); } }; ``` #### Go ```go func countSpecialNumbers(n int) int { digits := []int{} for n != 0 { digits = append(digits, n%10) n /= 10 } m := len(digits) vis := make([]bool, 10) ans := 0 for i := 1; i < m; i++ { ans += 9 * A(9, i-1) } for i := m - 1; i >= 0; i-- { v := digits[i] j := 0 if i == m-1 { j = 1 } for ; j < v; j++ { if !vis[j] { ans += A(10-(m-i), i) } } if vis[v] { break } vis[v] = true if i == 0 { ans++ } } return ans } func A(m, n int) int { if n == 0 { return 1 } return A(m, n-1) * (m - n + 1) } ``` ### 方法二 #### Python3 ```python class Solution: def countSpecialNumbers(self, n: int) -> int: return self.f(n) def f(self, n): @cache def dfs(pos, mask, lead, limit): if pos <= 0: return lead ^ 1 up = a[pos] if limit else 9 ans = 0 for i in range(up + 1): if (mask >> i) & 1: continue if i == 0 and lead: ans += dfs(pos - 1, mask, lead, limit and i == up) else: ans += dfs(pos - 1, mask | 1 << i, False, limit and i == up) return ans a = [0] * 11 l = 0 while n: l += 1 a[l] = n % 10 n //= 10 return dfs(l, 0, True, True) ``` #### Java ```java class Solution { private int[] a = new int[11]; private int[][] dp = new int[11][1 << 11]; public int countSpecialNumbers(int n) { return f(n); } private int f(int n) { for (var e : dp) { Arrays.fill(e, -1); } int len = 0; while (n > 0) { a[++len] = n % 10; n /= 10; } return dfs(len, 0, true, true); } private int dfs(int pos, int mask, boolean lead, boolean limit) { if (pos <= 0) { return lead ? 0 : 1; } if (!lead && !limit && dp[pos][mask] != -1) { return dp[pos][mask]; } int up = limit ? a[pos] : 9; int ans = 0; for (int i = 0; i <= up; ++i) { if (((mask >> i) & 1) == 1) { continue; } if (i == 0 && lead) { ans += dfs(pos - 1, mask, lead, limit && i == up); } else { ans += dfs(pos - 1, mask | 1 << i, false, limit && i == up); } } if (!lead && !limit) { dp[pos][mask] = ans; } return ans; } } ``` #### C++ ```cpp class Solution { public: int a[11]; int dp[11][1 << 11]; int countSpecialNumbers(int n) { return f(n); } int f(int n) { memset(dp, -1, sizeof dp); int len = 0; while (n) { a[++len] = n % 10; n /= 10; } return dfs(len, 0, true, true); } int dfs(int pos, int mask, bool lead, bool limit) { if (pos <= 0) { return lead ? 0 : 1; } if (!lead && !limit && dp[pos][mask] != -1) { return dp[pos][mask]; } int up = limit ? a[pos] : 9; int ans = 0; for (int i = 0; i <= up; ++i) { if ((mask >> i) & 1) continue; if (i == 0 && lead) { ans += dfs(pos - 1, mask, lead, limit && i == up); } else { ans += dfs(pos - 1, mask | 1 << i, false, limit && i == up); } } if (!lead && !limit) { dp[pos][mask] = ans; } return ans; } }; ``` #### Go ```go func countSpecialNumbers(n int) int { return f(n) } func f(n int) int { a := make([]int, 11) dp := make([][]int, 11) for i := range dp { dp[i] = make([]int, 1<<11) for j := range dp[i] { dp[i][j] = -1 } } l := 0 for n > 0 { l++ a[l] = n % 10 n /= 10 } var dfs func(int, int, bool, bool) int dfs = func(pos, mask int, lead, limit bool) int { if pos <= 0 { if lead { return 0 } return 1 } if !lead && !limit && dp[pos][mask] != -1 { return dp[pos][mask] } ans := 0 up := 9 if limit { up = a[pos] } for i := 0; i <= up; i++ { if ((mask >> i) & 1) == 1 { continue } if i == 0 && lead { ans += dfs(pos-1, mask, lead, limit && i == up) } else { ans += dfs(pos-1, mask|1<