LeetCode 1018. 可被 5 整除的二进制前缀 | Python

1018. 可被 5 整除的二进制前缀


题目来源:力扣(LeetCode)https://leetcode-cn.com/problems/binary-prefix-divisible-by-5/

题目


给定由若干 01 组成的数组 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. 1 <= A.length <= 30000
  2. A[i]01

解题思路


思路:模拟

先审题,题目给定一个数组 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=Ni1×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 1A.length30000,这里如果每次都需要计算保存 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=(Ri1×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 Ri1=Ni1mod5 成立,证明 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=(Ni1×2+A[i])mod5=(Ni1×2)mod5+A[i]mod5=(Ri1×2+A[i])mod5=(Ni1mod5×2+A[i])mod5=(Ni1mod5×2)mod5+A[i]mod5=(Ni1×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 0i<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

欢迎关注


公众号 【书所集录


如有错误,烦请指出,欢迎指点交流。若觉得写得还不错,麻烦点个赞👍,谢谢。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值