Skip to content

Commit a3f3374

Browse files
committed
周赛 477
1 parent e78c951 commit a3f3374

File tree

7 files changed

+85
-63
lines changed

7 files changed

+85
-63
lines changed

leetcode/weekly/477/README.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
# 力扣周赛 477 题解 - 灵茶山艾府 - 灵神
22

33
【题单+刷题路线】https://leetcode.cn/circle/discuss/RvFUtj/
4+
45
本场比赛题解 + Python/Java/C++/Go 代码:
5-
Q1 https://leetcode.cn/problems/concatenate-non-zero-digits-and-multiply-by-sum-i/solution/bu-yong-zi-fu-chuan-de-zuo-fa-pythonjava-0pai/
6-
Q2 https://leetcode.cn/problems/find-maximum-balanced-xor-subarray-length/solution/qian-zhui-he-yu-ha-xi-biao-pythonjavacgo-2504/
7-
Q3 https://leetcode.cn/problems/concatenate-non-zero-digits-and-multiply-by-sum-ii/solution/san-ge-qian-zhui-he-pythonjavacgo-by-end-6e1a/
8-
Q4 https://leetcode.cn/problems/number-of-effective-subsequences/solution/zheng-nan-ze-fan-rong-chi-yuan-li-sos-dp-abir/
6+
- Q1 https://leetcode.cn/problems/concatenate-non-zero-digits-and-multiply-by-sum-i/solution/bu-yong-zi-fu-chuan-de-zuo-fa-pythonjava-0pai/
7+
- Q2 https://leetcode.cn/problems/find-maximum-balanced-xor-subarray-length/solution/qian-zhui-he-yu-ha-xi-biao-pythonjavacgo-2504/
8+
- Q3 https://leetcode.cn/problems/concatenate-non-zero-digits-and-multiply-by-sum-ii/solution/san-ge-qian-zhui-he-pythonjavacgo-by-end-6e1a/
9+
- Q4 https://leetcode.cn/problems/number-of-effective-subsequences/solution/zheng-nan-ze-fan-rong-chi-yuan-li-sos-dp-abir/
910

10-
[本题视频讲解](https://www.bilibili.com/video/TODO时间/?t=2m30s),欢迎点赞关注~
11+
[本题视频讲解](https://www.bilibili.com/video/BV1arUKBbEks/),欢迎点赞关注~

leetcode/weekly/477/a/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
对于不为零的数位 $d$,通过 $d\cdot 10^k$ 组合起来,其中 $k$ 是当前 $x$ 的十进制长度。$k$ 从 $0$ 开始,每添加一个数位就加一。
44

5-
下午两点 [B站@灵茶山艾府](https://space.bilibili.com/206214) 直播讲题,欢迎关注~
5+
[本题视频讲解](https://www.bilibili.com/video/BV1arUKBbEks/?t=48m9s),欢迎点赞关注~
66

77
```py [sol-Python3]
88
class Solution:

leetcode/weekly/477/b/README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ $s[r]\oplus s[l] = 0$ 意味着 $s[r] = s[l]$。
2020
- 如果在哈希表中查询到二元组,那么用当前下标减去哈希表中保存的下标,即为子数组长度,更新答案的最大值。
2121
- 否则,把二元组及当前下标保存到哈希表中。
2222

23-
下午两点 [B站@灵茶山艾府](https://space.bilibili.com/206214) 直播讲题,欢迎关注~
24-
2523
```py [sol-Python3]
2624
class Solution:
2725
def maxBalancedSubarray(self, nums: List[int]) -> int:
@@ -120,7 +118,7 @@ func maxBalancedSubarray(nums []int) (ans int) {
120118

121119
## 专题训练
122120

123-
见下面动态规划题单的**§1.2 前缀和与哈希表**」。
121+
见下面数据结构题单的**§1.2 前缀和与哈希表**」。
124122

125123
## 分类题单
126124

leetcode/weekly/477/c/README.md

Lines changed: 37 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88

99
我们可以先计算 $s$ 的每个前缀(包括空前缀)对应的数字,即 $\textit{preNum} = [0,1,12,123,1234,12345]$。
1010

11-
想一想,如何得到 $34$?
11+
想一想,如何从这些数中得到 $34$?
1212

13-
我们可以计算 $1234 - 12\cdot 10^2 = 1234-1200 = 34$。其中 $2$ 是子串的长度
13+
我们可以计算 $1234 - 12\cdot 10^2 = 1234-1200 = 34$。其中 $2$ 是子串 $34$ 的长度
1414

1515
一般地,子串 $[l,r]$ 对应的数字为
1616

@@ -22,11 +22,11 @@ $$
2222

2323
这意味着我们还需要计算子串中的非零字符个数,代替上式中的 $r-l+1$。
2424

25-
维护把非零字符视作 $1$,计算其前缀和。
25+
把 $s$ 中的非零字符视作 $1$,计算其前缀和。
2626

27-
注意取模,原理见 [模运算的世界:当加减乘除遇上取模](https://leetcode.cn/circle/discuss/mDfnkW/)
27+
代码实现时,注意取模。为什么可以在中途取模?原理见 [模运算的世界:当加减乘除遇上取模](https://leetcode.cn/circle/discuss/mDfnkW/)
2828

29-
下午两点 [B站@灵茶山艾府](https://space.bilibili.com/206214) 直播讲题,欢迎关注~
29+
[本题视频讲解](https://www.bilibili.com/video/BV1arUKBbEks/?t=52m5s),欢迎点赞关注~
3030

3131
```py [sol-Python3]
3232
MOD = 1_000_000_007
@@ -40,17 +40,17 @@ for i in range(1, MAX_N):
4040
class Solution:
4141
def sumAndMultiply(self, s: str, queries: List[List[int]]) -> List[int]:
4242
n = len(s)
43-
sum_d = [0] * (n + 1) # s 的前缀和
44-
pre_num = [0] * (n + 1) # s 的前缀对应的数字(模 MOD)
45-
sum_non_zero = [0] * (n + 1) # s 的前缀的非零数字个数
43+
sum_d = [0] * (n + 1) # s 的前缀和
44+
pre_num = [0] * (n + 1) # s 的前缀对应的数字(模 MOD)
45+
sum_non_zero = [0] * (n + 1) # s 的前缀中的非零数字个数
4646
for i, d in enumerate(map(int, s)):
4747
sum_d[i + 1] = sum_d[i] + d
4848
pre_num[i + 1] = (pre_num[i] * 10 + d) % MOD if d else pre_num[i]
4949
sum_non_zero[i + 1] = sum_non_zero[i] + (d > 0)
5050

5151
ans = []
5252
for l, r in queries:
53-
r += 1 # 注意这里把 r 加一了
53+
r += 1 # 避免下面多次计算 r+1
5454
length = sum_non_zero[r] - sum_non_zero[l]
5555
x = pre_num[r] - pre_num[l] * pow10[length]
5656
ans.append(x * (sum_d[r] - sum_d[l]) % MOD)
@@ -62,8 +62,15 @@ class Solution {
6262
private static final int MOD = 1_000_000_007;
6363
private static final int MAX_N = 100_001;
6464
private static final int[] pow10 = new int[MAX_N];
65+
private static boolean initialized = false;
66+
67+
// 这样写比 static block 快
68+
private void init() {
69+
if (initialized) {
70+
return;
71+
}
72+
initialized = true;
6573

66-
static {
6774
// 预处理 10 的幂
6875
pow10[0] = 1;
6976
for (int i = 1; i < MAX_N; i++) {
@@ -72,24 +79,26 @@ class Solution {
7279
}
7380

7481
public int[] sumAndMultiply(String s, int[][] queries) {
82+
init();
83+
7584
int n = s.length();
7685
int[] sumD = new int[n + 1]; // s 的前缀和
7786
int[] preNum = new int[n + 1]; // s 的前缀对应的数字(模 mod)
78-
int[] sumNonZero = new int[n + 1]; // s 的前缀的非零数字个数
87+
int[] sumNonZero = new int[n + 1]; // s 的前缀中的非零数字个数
7988
for (int i = 0; i < n; i++) {
8089
int d = s.charAt(i) - '0';
81-
sumD[i + 1] = sumD[i] + d; // s 的前缀和
82-
preNum[i + 1] = d > 0 ? (int) ((preNum[i] * 10L + d) % MOD) : preNum[i]; // s 的前缀对应的数字(模 MOD)
83-
sumNonZero[i + 1] = sumNonZero[i] + (d > 0 ? 1 : 0); // s 的前缀的非零数字个数
90+
sumD[i + 1] = sumD[i] + d;
91+
preNum[i + 1] = d > 0 ? (int) ((preNum[i] * 10L + d) % MOD) : preNum[i];
92+
sumNonZero[i + 1] = sumNonZero[i] + (d > 0 ? 1 : 0);
8493
}
8594

8695
int[] ans = new int[queries.length];
8796
for (int i = 0; i < queries.length; i++) {
8897
int l = queries[i][0];
89-
int r = queries[i][1] + 1; // 注意这里把 r 加一了
98+
int r = queries[i][1] + 1; // 注意这里已经把 r 加一了
9099
int length = sumNonZero[r] - sumNonZero[l];
91-
long x = preNum[r] - (long) preNum[l] * pow10[length] % MOD; // 注意结果可能是负数,所以下面 +MOD
92-
ans[i] = (int) ((x + MOD) * (sumD[r] - sumD[l]) % MOD);
100+
long x = preNum[r] - (long) preNum[l] * pow10[length] % MOD + MOD; // +MOD 保证结果非负
101+
ans[i] = (int) (x * (sumD[r] - sumD[l]) % MOD);
93102
}
94103
return ans;
95104
}
@@ -119,16 +128,16 @@ public:
119128
int d = s[i] - '0';
120129
sum_d[i + 1] = sum_d[i] + d; // s 的前缀和
121130
pre_num[i + 1] = d > 0 ? (pre_num[i] * 10LL + d) % MOD : pre_num[i]; // s 的前缀对应的数字(模 MOD)
122-
sum_non_zero[i + 1] = sum_non_zero[i] + (d > 0); // s 的前缀的非零数字个数
131+
sum_non_zero[i + 1] = sum_non_zero[i] + (d > 0); // s 的前缀中的非零数字个数
123132
}
124133

125134
vector<int> ans;
126135
ans.reserve(queries.size()); // 预分配空间
127136
for (auto& q : queries) {
128-
int l = q[0], r = q[1] + 1; // 注意这里把 r 加一了
137+
int l = q[0], r = q[1] + 1; // 注意这里已经把 r 加一了
129138
int length = sum_non_zero[r] - sum_non_zero[l];
130-
long long x = pre_num[r] - 1LL * pre_num[l] * pow10[length] % MOD; // 注意结果可能是负数,所以下面 +mod
131-
ans.push_back((x + MOD) * (sum_d[r] - sum_d[l]) % MOD);
139+
long long x = pre_num[r] - 1LL * pre_num[l] * pow10[length] % MOD + MOD; // +MOD 保证结果非负
140+
ans.push_back(x * (sum_d[r] - sum_d[l]) % MOD);
132141
}
133142
return ans;
134143
}
@@ -152,7 +161,7 @@ func sumAndMultiply(s string, queries [][]int) []int {
152161
n := len(s)
153162
sumD := make([]int, n+1) // s 的前缀和
154163
preNum := make([]int, n+1) // s 的前缀对应的数字(模 mod)
155-
sumNonZero := make([]int, n+1) // s 的前缀的非零数字个数
164+
sumNonZero := make([]int, n+1) // s 的前缀中的非零数字个数
156165
for i, ch := range s {
157166
d := int(ch - '0')
158167
sumD[i+1] = sumD[i] + d
@@ -168,8 +177,8 @@ func sumAndMultiply(s string, queries [][]int) []int {
168177
for i, q := range queries {
169178
l, r := q[0], q[1]+1
170179
length := sumNonZero[r] - sumNonZero[l]
171-
x := preNum[r] - preNum[l]*pow10[length]%mod // 注意结果可能是负数,所以下面 +mod
172-
ans[i] = (x + mod) * (sumD[r] - sumD[l]) % mod
180+
x := preNum[r] - preNum[l]*pow10[length]%mod + mod // +mod 保证结果非负
181+
ans[i] = x * (sumD[r] - sumD[l]) % mod
173182
}
174183
return ans
175184
}
@@ -182,6 +191,10 @@ func sumAndMultiply(s string, queries [][]int) []int {
182191
- 时间复杂度:$\mathcal{O}(n+q)$,其中 $n$ 是 $\textit{nums}$ 的长度,$q$ 是 $\textit{queries}$ 的长度。
183192
- 空间复杂度:$\mathcal{O}(n)$。返回值不计入。
184193

194+
## 相似题目
195+
196+
[2156. 查找给定哈希值的子串](https://leetcode.cn/problems/find-substring-with-given-hash-value/)
197+
185198
## 专题训练
186199

187200
见下面数据结构题单的「**一、前缀和**」。

leetcode/weekly/477/c/c.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ func sumAndMultiply(s string, queries [][]int) []int {
1717
n := len(s)
1818
sumD := make([]int, n+1) // s 的前缀和
1919
preNum := make([]int, n+1) // s 的前缀对应的数字(模 mod)
20-
sumNonZero := make([]int, n+1) // s 的前缀的非零数字个数
20+
sumNonZero := make([]int, n+1) // s 的前缀中的非零数字个数
2121
for i, ch := range s {
2222
d := int(ch - '0')
2323
sumD[i+1] = sumD[i] + d
@@ -33,8 +33,8 @@ func sumAndMultiply(s string, queries [][]int) []int {
3333
for i, q := range queries {
3434
l, r := q[0], q[1]+1
3535
length := sumNonZero[r] - sumNonZero[l]
36-
x := preNum[r] - preNum[l]*pow10[length]%mod // 注意结果可能是负数,所以下面 +mod
37-
ans[i] = (x + mod) * (sumD[r] - sumD[l]) % mod
36+
x := preNum[r] - preNum[l]*pow10[length]%mod + mod // +mod 保证结果非负
37+
ans[i] = x * (sumD[r] - sumD[l]) % mod
3838
}
3939
return ans
4040
}

leetcode/weekly/477/d/README.md

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ $$
6161

6262
代码实现时,注意取模。为什么可以在中途取模?见 [模运算的世界:当加减乘除遇上取模](https://leetcode.cn/circle/discuss/mDfnkW/)
6363

64-
下午两点 [B站@灵茶山艾府](https://space.bilibili.com/206214) 直播讲题,欢迎关注~
64+
[本题视频讲解](https://www.bilibili.com/video/BV1arUKBbEks/),欢迎点赞关注~
6565

6666
```py [sol-Python3]
6767
MOD = 1_000_000_007
@@ -78,18 +78,19 @@ class Solution:
7878
for x in nums:
7979
or_all |= x
8080

81-
# 优化:如果 nums 只有一种数字,
82-
# 那么当这个数大于 0 时,可以把整个数组去掉,得到 OR=0,否则无法去掉任何子序列
81+
# 优化:如果 nums 只有一种数字,可以把整个数组去掉,按位或 = 0 < or_all
8382
if len(set(nums)) == 1:
84-
return 1 if or_all else 0
83+
return 1
8584

86-
mx = or_all.bit_length()
87-
u = 1 << mx
85+
w = or_all.bit_length()
86+
u = 1 << w
8887
f = [0] * u
8988
for x in nums:
9089
f[x] += 1
91-
for i in range(mx):
90+
for i in range(w):
9291
bit = 1 << i # 避免在循环中反复计算 1 << i
92+
if or_all & bit == 0: # 优化:or_all 中是 0 的比特位无需计算
93+
continue
9394
s = 0
9495
while s < u:
9596
s |= bit # 快速跳到第 i 位是 1 的 s
@@ -138,13 +139,16 @@ class Solution {
138139
or |= x;
139140
}
140141

141-
int mx = 32 - Integer.numberOfLeadingZeros(or);
142-
int[] f = new int[1 << mx];
142+
int w = 32 - Integer.numberOfLeadingZeros(or);
143+
int[] f = new int[1 << w];
143144
for (int x : nums) {
144145
f[x]++;
145146
}
146-
for (int i = 0; i < mx; i++) {
147-
for (int s = 0; s < (1 << mx); s++) {
147+
for (int i = 0; i < w; i++) {
148+
if ((or >> i & 1) == 0) { // 优化:or 中是 0 的比特位无需计算
149+
continue;
150+
}
151+
for (int s = 0; s < (1 << w); s++) {
148152
s |= 1 << i;
149153
f[s] += f[s ^ (1 << i)];
150154
}
@@ -181,18 +185,18 @@ int init = [] {
181185
class Solution {
182186
public:
183187
int countEffective(vector<int>& nums) {
184-
int or_ = 0;
185-
for (int x : nums) {
186-
or_ |= x;
187-
}
188+
int or_ = reduce(nums.begin(), nums.end(), 0, bit_or<>());
189+
int w = bit_width((uint32_t) or_);
188190

189-
int mx = bit_width((uint32_t) or_);
190-
vector<int> f(1 << mx);
191+
vector<int> f(1 << w);
191192
for (int x : nums) {
192193
f[x]++;
193194
}
194-
for (int i = 0; i < mx; i++) {
195-
for (int s = 0; s < (1 << mx); s++) {
195+
for (int i = 0; i < w; i++) {
196+
if ((or_ >> i & 1) == 0) { // 优化:or_ 中是 0 的比特位无需计算
197+
continue;
198+
}
199+
for (int s = 0; s < (1 << w); s++) {
196200
s |= 1 << i;
197201
f[s] += f[s ^ (1 << i)];
198202
}
@@ -231,13 +235,16 @@ func countEffective(nums []int) int {
231235
or |= x
232236
}
233237

234-
mx := bits.Len(uint(or))
235-
f := make([]int, 1<<mx)
238+
w := bits.Len(uint(or))
239+
f := make([]int, 1<<w)
236240
for _, x := range nums {
237241
f[x]++
238242
}
239-
for i := range mx {
240-
for s := 0; s < 1<<mx; s++ {
243+
for i := range w {
244+
if or>>i&1 == 0 { // 优化:or 中是 0 的比特位无需计算
245+
continue
246+
}
247+
for s := 0; s < 1<<w; s++ {
241248
s |= 1 << i
242249
f[s] += f[s^1<<i]
243250
}

leetcode/weekly/477/d/d.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,16 @@ func countEffective(nums []int) int {
2121
or |= x
2222
}
2323

24-
mx := bits.Len(uint(or))
25-
f := make([]int, 1<<mx)
24+
w := bits.Len(uint(or))
25+
f := make([]int, 1<<w)
2626
for _, x := range nums {
2727
f[x]++
2828
}
29-
for i := range mx {
30-
for s := 0; s < 1<<mx; s++ {
29+
for i := range w {
30+
if or>>i&1 == 0 { // 优化:or 中是 0 的比特位无需计算
31+
continue
32+
}
33+
for s := 0; s < 1<<w; s++ {
3134
s |= 1 << i
3235
f[s] += f[s^1<<i]
3336
}

0 commit comments

Comments
 (0)