Binary Prefix Divisible By 5
EasyPrompt
You are given a binary array nums (0-indexed).
We define xi as the number whose binary representation is the subarray nums[0..i] (from most-significant-bit to least-significant-bit).
- For example, if
nums = [1,0,1], thenx0 = 1,x1 = 2, andx2 = 5.
Return an array of booleans answer where answer[i] is true if xi is divisible by 5.
Example 1:
Input: nums = [0,1,1]
Output: [true,false,false]
Explanation: The input numbers in binary are 0, 01, 011; which are 0, 1, and 3 in base-10.
Only the first number is divisible by 5, so answer[0] is true.Example 2:
Input: nums = [1,1,1]
Output: [false,false,false]
Constraints:
1 <= nums.length <= 105nums[i]is either0or1.
Approaches
2 approaches with complexity analysis and trade-offs.
A much more efficient approach avoids dealing with large numbers altogether by using modular arithmetic. The key insight is that to check for divisibility by 5, we only need the remainder of the number when divided by 5, not the number itself. We can maintain this remainder as we iterate through the binary array.
Algorithm
- Create an empty
List<Boolean>calledanswer. - Initialize an integer
remainderto 0. - Loop through each
bitin thenumsarray. - Update the
remainderusing the formula:remainder = (remainder * 2 + bit) % 5. - Check if the new
remainderis 0. - Add
truetoanswerifremainderis 0, otherwise addfalse. - After the loop, return
answer.
Walkthrough
The recurrence relation for the number x_i formed by the prefix nums[0...i] is x_i = x_{i-1} * 2 + nums[i]. We are interested in x_i % 5. Using properties of modular arithmetic, we can write: x_i % 5 = ( (x_{i-1} * 2) + nums[i] ) % 5. This can be further broken down: x_i % 5 = ( (x_{i-1} % 5) * 2 + nums[i] ) % 5. Let rem_i = x_i % 5. The formula becomes rem_i = (rem_{i-1} * 2 + nums[i]) % 5. This means we can calculate the remainder for the current prefix using only the remainder from the previous prefix. The remainder will always be a small integer (0, 1, 2, 3, or 4), so we can use a standard integer variable to keep track of it, completely avoiding large number arithmetic and potential overflows. The algorithm iterates through the nums array, updating the remainder at each step using this formula. If the remainder becomes 0, the number is divisible by 5.
import java.util.ArrayList;import java.util.List; class Solution { public List<Boolean> prefixesDivBy5(int[] nums) { List<Boolean> answer = new ArrayList<>(); int remainder = 0; for (int bit : nums) { // Update remainder: remainder = (remainder * 2 + bit) % 5 // This can be written as a left shift for multiplication by 2 remainder = ((remainder << 1) | bit) % 5; answer.add(remainder == 0); } return answer; }}Complexity
Time
O(n). We iterate through the input array of length `n` exactly once. Each step inside the loop involves a few constant-time arithmetic operations.
Space
O(n). The space is dominated by the output list `answer`, which stores `n` boolean values. The auxiliary space used by the algorithm (for the `remainder` variable) is `O(1)`.
Trade-offs
Pros
Extremely efficient with linear time complexity.
Constant auxiliary space complexity.
Avoids all issues with large numbers and overflows.
Cons
Requires knowledge of modular arithmetic to come up with the solution.
This approach directly simulates the process described in the problem. For each prefix of the input array nums, we form the corresponding binary number. Since these numbers can become extremely large and exceed the capacity of standard integer types like long, we use Java's BigInteger class, which can handle arbitrarily large integers. We iterate through the array, build the number for each prefix, and check its divisibility by 5.
Algorithm
- Initialize an empty list of booleans,
answer. - Initialize a
BigIntegervariable,currentNum, to zero. - Iterate through the input array
numsfrom left to right. - In each step
i, updatecurrentNumto represent the binary number formed bynums[0...i]. The new number is calculated from the previous one by shifting it left by one bit (multiplying by 2) and then adding the current bitnums[i]. The formula iscurrentNum = currentNum * 2 + nums[i]. - After updating
currentNum, check if it's divisible by 5 using the modulo operator.currentNum.mod(BigInteger.valueOf(5))will be zero if it is divisible. - Append
trueto theanswerlist if it's divisible, andfalseotherwise. - After iterating through all the numbers in
nums, return theanswerlist.
Walkthrough
We initialize an empty list of booleans, answer, to store the results. We also initialize a BigInteger variable, let's call it currentNum, to zero. We iterate through the input array nums from left to right. In each step i, we update currentNum to represent the binary number formed by nums[0...i]. The new number is calculated from the previous one by shifting it left by one bit (multiplying by 2) and then adding the current bit nums[i]. The formula is currentNum = currentNum * 2 + nums[i]. After updating currentNum, we check if it's divisible by 5 using the modulo operator. currentNum.mod(BigInteger.valueOf(5)) will be zero if it is divisible. We append true to our answer list if it's divisible, and false otherwise. After iterating through all the numbers in nums, we return the answer list. This approach is correct but inefficient for large inputs due to the overhead of BigInteger arithmetic.
import java.math.BigInteger;import java.util.ArrayList;import java.util.List; class Solution { public List<Boolean> prefixesDivBy5(int[] nums) { List<Boolean> answer = new ArrayList<>(); BigInteger currentNum = BigInteger.ZERO; BigInteger five = BigInteger.valueOf(5); BigInteger two = BigInteger.valueOf(2); for (int bit : nums) { // currentNum = currentNum * 2 + bit currentNum = currentNum.multiply(two).add(BigInteger.valueOf(bit)); // Check if currentNum % 5 == 0 if (currentNum.mod(five).equals(BigInteger.ZERO)) { answer.add(true); } else { answer.add(false); } } return answer; }}Complexity
Time
O(n^2). The loop runs `n` times. Inside the loop, `currentNum` has approximately `i` bits at step `i`. `BigInteger` operations like multiplication and addition take time proportional to the number of bits. So, the `i`-th iteration takes `O(i)` time. The total time is the sum of `O(i)` for `i` from 1 to `n`, which results in `O(n^2)`.
Space
O(n). The `BigInteger` `currentNum` can grow to have up to `n` bits, requiring `O(n)` space. The output list `answer` also requires `O(n)` space.
Trade-offs
Pros
Conceptually straightforward and easy to understand.
Directly translates the problem statement into code.
Cons
Inefficient for large inputs. The
O(n^2)time complexity will lead to a "Time Limit Exceeded" error given the constraintn <= 10^5.High memory usage due to storing the full large number in
BigInteger.
Solutions
Solution
class Solution {public List<Boolean> prefixesDivBy5(int[] nums) { List<Boolean> ans = new ArrayList<>(); int x = 0; for (int v : nums) { x = (x << 1 | v) % 5; ans.add(x == 0); } return ans; }}class Solution {public: vector<bool> prefixesDivBy5(vector<int> &nums) { vector<bool> ans; int x = 0; for (int v : nums) { x = (x << 1 | v) % 5; ans.push_back(x == 0); } return ans; }};class Solution: def prefixesDivBy5(self, nums: List[int]) -> List[bool]: ans = [] x = 0 for v in nums: x = (x << 1 | v) % 5 ans . append(x == 0) return ansVideo walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.