1018. 可被 5 整除的二进制前缀
题目来源:力扣(LeetCode)https://leetcode-cn.com/problems/binary-prefix-divisible-by-5/
题目
给定由若干 0
和 1
组成的数组 A
。我们定义 N_i
:从 A[0]
到 A[i]
的第 i
个子数组被解释为一个二进制数(从最高有效位到最低有效位)。
返回布尔值列表 answer
,只有当 N_i
可以被 5
整除时,答案 answer[i]
为 true
,否则为 false
。
示例 1:
输入:[0,1,1]
输出:[true,false,false]
解释:
输入数字为 0, 01, 011;也就是十进制中的 0, 1, 3 。只有第一个数可以被 5 整除,因此 answer[0] 为真。
示例 2:
输入:[1,1,1]
输出:[false,false,false]
示例 3:
输入:[0,1,1,1,1,1]
输出:[true,false,false,false,true,false]
示例 4:
输入:[1,1,1,0,1]
输出:[false,false,false,false,false]
提示:
1 <= A.length <= 30000
A[i]
为0
或1
解题思路
思路:模拟
先审题,题目给定一个数组 A A A,其中元素由若干个 0 0 0 或 1 1 1 组成。
题目定义 N i N_i Ni:表示从 A [ 0 ] A[0] A[0] 到 A [ i ] A[i] A[i] 的第 i i i 个子数组被解释为一个二进制。(由高位到低位)。
题目要求返回一个布尔值列表 a n s w e r \rm{answer} answer,其中值由 N i N_i Ni 是否能被 5 5 5 整除来决定,若能被整除,值为 T r u e True True,否则为 F a l s e False False。
这里再说下关于 N i N_i Ni 的定义,结合示例 1 1 1 来看:
输入:[0,1,1]
输出:[true,false,false]
-
在这里 N 0 N_0 N0 为 0 0 0,对应十进制数 0 0 0;
-
N 1 N_1 N1 为 01 01 01,这里表示由数组 A A A 索引 0 0 0 到 1 1 1 两个元素组成的二进制数,对应十进制数 1 1 1;
-
那么 N 2 N_2 N2 在这里为 011 011 011,对应十进制数 3 3 3。
由上面的例子,我们也可以看到:
- 当 i = 0 i = 0 i=0 时, N 0 = A [ 0 ] N_0 = A[0] N0=A[0] ;
- 当 i > 0 i > 0 i>0 时, N i = N i − 1 × 2 + A [ i ] N_i=N_{i-1} \times 2 + A[i] Ni=Ni−1×2+A[i] 。
也就是说依次计算每个 N i N_i Ni 的值,判断是否被 5 5 5 整除即可。
但由于题目后面提示数组 A A A 的长度 1 ≤ A . l e n g t h ≤ 30000 1\leq A.length \leq 30000 1≤A.length≤30000,这里如果每次都需要计算保存 N i N_i Ni 的值,可能会导致溢出。
因为我们只需要知道 N i N_i Ni 是否能够被 5 5 5 整除,那么在计算的过程中考虑只保留余数。因为在这里余数只可能是 [ 0 , 1 , 2 , 3 , 4 ] [0, 1, 2, 3, 4] [0,1,2,3,4] 之中的其中一个,不用担心溢出的问题。
现在用 R i R_i Ri 表示计算下标 i i i 时除以 5 5 5 的余数。因为数组 A A A 的元素只有 0 0 0 或 1 1 1,那么有 R 0 = A [ 0 ] R_0 = A[0] R0=A[0],当 i > 0 i>0 i>0 时,则有 R i = ( R i − 1 × 2 + A [ i ] ) m o d 5 R_i = (R_{i-1} \times 2 + A[i])\; \rm{mod} \;5 Ri=(Ri−1×2+A[i])mod5,那么在这里只需要判断每个 R i R_i Ri 是否为 0 0 0 即可。
现在主要要证明这个仅判断余数是否为 0 0 0,能否成立?这里用数学归纳法来证明。
当 i = 0 i= 0 i=0 时,由于 N 0 = A [ 0 ] N_0=A[0] N0=A[0],而 R 0 = A [ 0 ] R_0=A[0] R0=A[0],可得 R 0 = N 0 R_0=N_0 R0=N0 且 A [ 0 ] A[0] A[0] 仅可能为 0 0 0 或 1 1 1,那么 R 0 = N 0 m o d 5 R_0 = N_0\;\rm{mod}\;5 R0=N0mod5 成立;
当
i
>
0
i > 0
i>0 时,现在假设
R
i
−
1
=
N
i
−
1
m
o
d
5
R_{i-1}=N_{i-1}\;\rm{mod}\;5
Ri−1=Ni−1mod5 成立,证明
R
i
=
N
i
m
o
d
5
R_{i}=N_{i}\;\rm{mod}\;5
Ri=Nimod5 成立:
N
i
m
o
d
5
=
(
N
i
−
1
×
2
+
A
[
i
]
)
m
o
d
5
=
(
N
i
−
1
×
2
)
m
o
d
5
+
A
[
i
]
m
o
d
5
R
i
=
(
R
i
−
1
×
2
+
A
[
i
]
)
m
o
d
5
=
(
N
i
−
1
m
o
d
5
×
2
+
A
[
i
]
)
m
o
d
5
=
(
N
i
−
1
m
o
d
5
×
2
)
m
o
d
5
+
A
[
i
]
m
o
d
5
=
(
N
i
−
1
×
2
)
m
o
d
5
+
A
[
i
]
m
o
d
5
\begin{aligned} N_{i}\;\rm{mod}\;5 &= (N_{i-1}\times2+A[i])\;\rm{mod}\;5 \\ &= (N_{i-1}\times2)\;\rm{mod}\;5 + A[i] \;\rm{mod}\;5 \\ \\ R_i &= (R_{i-1} \times 2 + A[i])\;\rm{mod}\;5 \\ &= (N_{i-1} \;\rm{mod}\;5 \times 2 + A[i]) \;\rm{mod}\;5 \\ &= (N_{i-1} \;\rm{mod}\;5 \times 2) \;\rm{mod}\;5 + A[i] \;\rm{mod}\;5 \\ &= (N_{i-1} \times 2) \;\rm{mod}\;5 + A[i] \;\rm{mod}\;5 \\ \end{aligned}
Nimod5Ri=(Ni−1×2+A[i])mod5=(Ni−1×2)mod5+A[i]mod5=(Ri−1×2+A[i])mod5=(Ni−1mod5×2+A[i])mod5=(Ni−1mod5×2)mod5+A[i]mod5=(Ni−1×2)mod5+A[i]mod5
由此可得,
R
i
=
N
i
m
o
d
5
R_i = N_i \;\rm{mod}\;5
Ri=Nimod5 成立。
将上面的情况结合起来,也就说当 0 ≤ i < n 0\leq i < n 0≤i<n 时, R i = N i m o d 5 R_i = N_i \;\rm{mod}\;5 Ri=Nimod5 恒成立。那么只要计算 R i R_i Ri 的值判断是否为 0 即可。
具体的代码实现如下。
class Solution:
def prefixesDivBy5(self, A: List[int]) -> List[bool]:
answer = []
rem = 0
for num in A:
rem = ((rem << 1) + num) % 5
answer.append(rem == 0)
return answer
欢迎关注
公众号 【书所集录】
如有错误,烦请指出,欢迎指点交流。若觉得写得还不错,麻烦点个赞👍,谢谢。