Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add solutions to lc problem: No.0600 #3362

Merged
merged 1 commit into from
Aug 5, 2024
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 @@ -25,7 +25,7 @@ tags:
<pre>
<strong>输入:</strong> n = 5
<strong>输出:</strong> 5
<strong>解释:</strong>
<strong>解释:</strong>
下面列出范围在 [0, 5] 的非负整数与其对应的二进制表示:
0 : 0
1 : 1
Expand Down Expand Up @@ -67,30 +67,28 @@ tags:

这道题实际上是求在给定区间 $[l,..r]$ 中,数字的二进制表示不包含连续的 $1$ 的个数。个数与数的位数以及每个二进制位上的数字有关。我们可以用数位 DP 的思路来解决这道题。数位 DP 中,数的大小对复杂度的影响很小。

对于区间 $[l,..r]$ 问题,我们一般会将其转化为 $[1,..r]$ 然后再减去 $[1,..l - 1]$ 的问题,即:
对于区间 $[l,..r]$ 问题,我们一般会将其转化为 $[0,..r]$ 然后再减去 $[0,..l - 1]$ 的问题,即:

$$
ans = \sum_{i=1}^{r} ans_i - \sum_{i=1}^{l-1} ans_i
ans = \sum_{i=0}^{r} ans_i - \sum_{i=0}^{l-1} ans_i
$$

不过对于本题而言,我们只需要求出区间 $[0,..r]$ 的值即可。

这里我们用记忆化搜索来实现数位 DP。从起点向下搜索,到最底层得到方案数,一层层向上返回答案并累加,最后从搜索起点得到最终的答案。
这里我们用记忆化搜索来实现数位 DP。基本步骤如下:

基本步骤如下:

1. 将数字 $n$ 转为二进制数组 $a$,其中 $a[1]$ 为最低位,而 $a[len]$ 为最高位;
1. 根据题目信息,设计函数 $dfs()$,对于本题,我们定义 $dfs(pos, pre, limit)$,答案为 $dfs(len, 1, true)$。
1. 将数字 $n$ 转为二进制字符串 $s$;
1. 根据题目信息,设计函数 $\textit{dfs}()$,对于本题,我们定义 $\textit{dfs}(\textit{pos}, \textit{pre}, \textit{limit})$,答案为 $\textit{dfs}(\textit{0}, 0, \textit{true})$。

其中:

- `pos` 表示数字的位数,从末位或者第一位开始,一般根据题目的数字构造性质来选择顺序。对于本题,我们选择从高位开始,因此,`pos` 的初始值为 `len`
- `pos` 表示数字的位数,我们从数字的最高位开始,即二进制字符串的首字符
- `pre` 表示当前数字二进制位上的数字,对于本题,`pre` 的初始值为 `0`;
- `limit` 表示可填的数字的限制,如果无限制,那么可以选择 $[0,1]$,否则,只能选择 $[0,..a[pos]]$。如果 `limit` 为 `true` 且已经取到了能取到的最大值,那么下一个 `limit` 同样为 `true`;如果 `limit` 为 `true` 但是还没有取到最大值,或者 `limit` 为 `false`,那么下一个 `limit` 为 `false`
- `limit` 表示可填的数字的限制,如果无限制,那么可以选择 $[0,1]$,否则,只能选择 $[0,..s[\textit{pos}]]$。

关于函数的实现细节,可以参考下面的代码。

时间复杂度 $O(\log n)$。
时间复杂度 $O(\log n)$,空间复杂度 $O(\log n)$。其中 $n$ 为题目给定的数字

相似题目:

Expand All @@ -109,61 +107,50 @@ $$
class Solution:
def findIntegers(self, n: int) -> int:
@cache
def dfs(pos, pre, limit):
if pos <= 0:
def dfs(pos: int, pre: int, limit: bool) -> int:
if pos == len(s):
return 1
up = a[pos] if limit else 1
up = int(s[pos]) if limit else 1
ans = 0
for i in range(up + 1):
if pre == 1 and i == 1:
continue
ans += dfs(pos - 1, i, limit and i == up)
ans += dfs(pos + 1, i, limit and i == up)
return ans

a = [0] * 33
l = 0
while n:
l += 1
a[l] = n & 1
n >>= 1
return dfs(l, 0, True)
s = bin(n)[2:]
return dfs(0, 0, True)
```

#### Java

```java
class Solution {
private int[] a = new int[33];
private int[][] dp = new int[33][2];
private char[] s;
private Integer[][] f;

public int findIntegers(int n) {
int len = 0;
while (n > 0) {
a[++len] = n & 1;
n >>= 1;
}
for (var e : dp) {
Arrays.fill(e, -1);
}
return dfs(len, 0, true);
s = Integer.toBinaryString(n).toCharArray();
f = new Integer[s.length][2];
return dfs(0, 0, true);
}

private int dfs(int pos, int pre, boolean limit) {
if (pos <= 0) {
if (pos >= s.length) {
return 1;
}
if (!limit && dp[pos][pre] != -1) {
return dp[pos][pre];
if (!limit && f[pos][pre] != null) {
return f[pos][pre];
}
int up = limit ? a[pos] : 1;
int up = limit ? s[pos] - '0' : 1;
int ans = 0;
for (int i = 0; i <= up; ++i) {
if (!(pre == 1 && i == 1)) {
ans += dfs(pos - 1, i, limit && i == up);
ans += dfs(pos + 1, i, limit && i == up);
}
}
if (!limit) {
dp[pos][pre] = ans;
f[pos][pre] = ans;
}
return ans;
}
Expand All @@ -175,37 +162,32 @@ class Solution {
```cpp
class Solution {
public:
int a[33];
int dp[33][2];

int findIntegers(int n) {
int len = 0;
while (n) {
a[++len] = n & 1;
n >>= 1;
}
memset(dp, -1, sizeof dp);
return dfs(len, 0, true);
}

int dfs(int pos, int pre, bool limit) {
if (pos <= 0) {
return 1;
}
if (!limit && dp[pos][pre] != -1) {
return dp[pos][pre];
}
int ans = 0;
int up = limit ? a[pos] : 1;
for (int i = 0; i <= up; ++i) {
if (!(pre == 1 && i == 1)) {
ans += dfs(pos - 1, i, limit && i == up);
string s = bitset<32>(n).to_string();
s = s.substr(s.find('1'));
int m = s.size();
int f[m][2];
memset(f, -1, sizeof(f));
auto dfs = [&](auto&& dfs, int pos, int pre, bool limit) -> int {
if (pos >= m) {
return 1;
}
}
if (!limit) {
dp[pos][pre] = ans;
}
return ans;
if (!limit && f[pos][pre] != -1) {
return f[pos][pre];
}
int up = limit ? s[pos] - '0' : 1;
int ans = 0;
for (int i = 0; i <= up; ++i) {
if (!(pre == 1 && i == 1)) {
ans += dfs(dfs, pos + 1, i, limit && i == up);
}
}
if (!limit) {
f[pos][pre] = ans;
}
return ans;
};
return dfs(dfs, 0, 0, true);
}
};
```
Expand All @@ -214,41 +196,68 @@ public:

```go
func findIntegers(n int) int {
a := make([]int, 33)
dp := make([][2]int, 33)
for i := range dp {
dp[i] = [2]int{-1, -1}
}
l := 0
for n > 0 {
l++
a[l] = n & 1
n >>= 1
s := strconv.FormatInt(int64(n), 2)
m := len(s)
f := make([][]int, m)
for i := range f {
f[i] = []int{-1, -1}
}
var dfs func(int, int, bool) int
dfs = func(pos, pre int, limit bool) int {
if pos <= 0 {
dfs = func(pos int, pre int, limit bool) int {
if pos >= m {
return 1
}
if !limit && dp[pos][pre] != -1 {
return dp[pos][pre]
if !limit && f[pos][pre] != -1 {
return f[pos][pre]
}
up := 1
if limit {
up = a[pos]
up = int(s[pos] - '0')
}
ans := 0
for i := 0; i <= up; i++ {
if !(pre == 1 && i == 1) {
ans += dfs(pos-1, i, limit && i == up)
ans += dfs(pos+1, i, limit && i == up)
}
}
if !limit {
dp[pos][pre] = ans
f[pos][pre] = ans
}
return ans
}
return dfs(l, 0, true)
return dfs(0, 0, true)
}
```

#### TypeScript

```ts
function findIntegers(n: number): number {
const s = n.toString(2);
const m = s.length;
const f: number[][] = Array.from({ length: m }, () => [-1, -1]);

function dfs(pos: number, pre: number, limit: boolean): number {
if (pos >= m) {
return 1;
}
if (!limit && f[pos][pre] !== -1) {
return f[pos][pre];
}
const up = limit ? parseInt(s[pos]) : 1;
let ans = 0;
for (let i = 0; i <= up; ++i) {
if (!(pre === 1 && i === 1)) {
ans += dfs(pos + 1, i, limit && i === up);
}
}
if (!limit) {
f[pos][pre] = ans;
}
return ans;
}

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

Expand Down
Loading
Loading