Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -61,32 +61,217 @@ edit_url: https://github.com/doocs/leetcode/edit/main/solution/3700-3799/3747.Co

<!-- solution:start -->

### 方法一
### 方法一:数位 DP

题目实际上是要我们统计区间 $[1, n]$ 之间,数字中不含 0 的整数个数。我们可以使用数位 DP 来解决这个问题。

我们设计一个函数 $\text{dfs}(i, \text{zero}, \text{lead}, \text{limit})$,表示当前处理到数字的第 $i$ 位,用 $\text{zero}$ 表示当前数字中是否已经出现过非零数字,用 $\text{lead}$ 表示当前是否还在处理前导零,而 $\text{limit}$ 表示当前数字是否受上界限制的方案数。答案为 $\text{dfs}(0, 0, 1, 1)$。

函数 $\text{dfs}(i, \text{zero}, \text{lead}, \text{limit})$ 中,如果 $i$ 大于等于数字的长度,那么我们就可以判断 $\text{zero}$ 和 $\text{lead}$,如果 $\text{zero}$ 为假且 $\text{lead}$ 为假,说明当前数字中不含 0,我们就返回 $1$,否则返回 $0$。

对于 $\text{dfs}(i, \text{zero}, \text{lead}, \text{limit})$,我们可以枚举当前数位 $d$ 的值,然后递归计算 $\text{dfs}(i+1, \text{nxt\_zero}, \text{nxt\_lead}, \text{nxt\_limit})$,其中 $\text{nxt\_zero}$ 表示当前数字中是否已经出现过非零数字,$\text{nxt\_lead}$ 表示当前是否还在处理前导零,而 $\text{nxt\_limit}$ 表示当前数字是否受上界限制。如果 $\text{limit}$ 为真,那么 $up$ 就是当前数位的上界,否则 $up$ 为 $9$。

时间复杂度 $O(\log_{10} n \times D)$,空间复杂度 $O(\log_{10} n)$。其中 $D$ 表示数字 0 到 9 的个数。

<!-- tabs:start -->

#### Python3

```python

class Solution:
def countDistinct(self, n: int) -> int:
@cache
def dfs(i: int, zero: bool, lead: bool, lim: bool) -> int:
if i >= len(s):
return 1 if (not zero and not lead) else 0
up = int(s[i]) if lim else 9
ans = 0
for j in range(up + 1):
nxt_zero = zero or (j == 0 and not lead)
nxt_lead = lead and j == 0
nxt_lim = lim and j == up
ans += dfs(i + 1, nxt_zero, nxt_lead, nxt_lim)
return ans

s = str(n)
return dfs(0, False, True, True)
```

#### Java

```java

class Solution {
private char[] s;
private Long[][][][] f;

public long countDistinct(long n) {
s = String.valueOf(n).toCharArray();
f = new Long[s.length][2][2][2];
return dfs(0, 0, 1, 1);
}

private long dfs(int i, int zero, int lead, int limit) {
if (i == s.length) {
return (zero == 0 && lead == 0) ? 1 : 0;
}

if (limit == 0 && f[i][zero][lead][limit] != null) {
return f[i][zero][lead][limit];
}

int up = limit == 1 ? s[i] - '0' : 9;
long ans = 0;
for (int d = 0; d <= up; d++) {
int nxtZero = zero == 1 || (d == 0 && lead == 0) ? 1 : 0;
int nxtLead = (lead == 1 && d == 0) ? 1 : 0;
int nxtLimit = (limit == 1 && d == up) ? 1 : 0;
ans += dfs(i + 1, nxtZero, nxtLead, nxtLimit);
}

if (limit == 0) {
f[i][zero][lead][limit] = ans;
}
return ans;
}
}
```

#### C++

```cpp

class Solution {
public:
long long countDistinct(long long n) {
string s = to_string(n);
int m = s.size();
static long long f[20][2][2][2];
memset(f, -1, sizeof(f));

auto dfs = [&](this auto&& dfs, int i, int zero, int lead, int limit) -> long long {
if (i == m) {
return (zero == 0 && lead == 0) ? 1 : 0;
}
if (!limit && f[i][zero][lead][limit] != -1) {
return f[i][zero][lead][limit];
}

int up = limit ? (s[i] - '0') : 9;
long long ans = 0;
for (int d = 0; d <= up; d++) {
int nxtZero = zero || (d == 0 && !lead);
int nxtLead = lead && d == 0;
int nxtLimit = limit && d == up;
ans += dfs(i + 1, nxtZero, nxtLead, nxtLimit);
}

if (!limit) f[i][zero][lead][limit] = ans;
return ans;
};

return dfs(0, 0, 1, 1);
}
};
```

#### Go

```go
func countDistinct(n int64) int64 {
s := []byte(fmt.Sprint(n))
m := len(s)
var f [20][2][2][2]int64
for i := range f {
for j := range f[i] {
for k := range f[i][j] {
for t := range f[i][j][k] {
f[i][j][k][t] = -1
}
}
}
}

var dfs func(i, zero, lead, limit int) int64
dfs = func(i, zero, lead, limit int) int64 {
if i == m {
if zero == 0 && lead == 0 {
return 1
}
return 0
}

if limit == 0 && f[i][zero][lead][limit] != -1 {
return f[i][zero][lead][limit]
}

up := 9
if limit == 1 {
up = int(s[i] - '0')
}

var ans int64 = 0
for d := 0; d <= up; d++ {
nxtZero := zero
if d == 0 && lead == 0 {
nxtZero = 1
}
nxtLead := 0
if lead == 1 && d == 0 {
nxtLead = 1
}
nxtLimit := 0
if limit == 1 && d == up {
nxtLimit = 1
}
ans += dfs(i+1, nxtZero, nxtLead, nxtLimit)
}

if limit == 0 {
f[i][zero][lead][limit] = ans
}
return ans
}

return dfs(0, 0, 1, 1)
}
```

#### TypeScript

```ts
function countDistinct(n: number): number {
const s = n.toString();
const m = s.length;

const f: number[][][][] = Array.from({ length: m }, () =>
Array.from({ length: 2 }, () => Array.from({ length: 2 }, () => Array(2).fill(-1))),
);

const dfs = (i: number, zero: number, lead: number, limit: number): number => {
if (i === m) {
return zero === 0 && lead === 0 ? 1 : 0;
}

if (limit === 0 && f[i][zero][lead][limit] !== -1) {
return f[i][zero][lead][limit];
}

const up = limit === 1 ? parseInt(s[i]) : 9;
let ans = 0;
for (let d = 0; d <= up; d++) {
const nxtZero = zero === 1 || (d === 0 && lead === 0) ? 1 : 0;
const nxtLead = lead === 1 && d === 0 ? 1 : 0;
const nxtLimit = limit === 1 && d === up ? 1 : 0;
ans += dfs(i + 1, nxtZero, nxtLead, nxtLimit);
}

if (limit === 0) {
f[i][zero][lead][limit] = ans;
}
return ans;
};

return dfs(0, 0, 1, 1);
}
```

<!-- tabs:end -->
Expand Down
Loading