title: Binary Prefix Divisible By 5 slug: binary-prefix-divisible-by-5 difficulty: easy leetcode_id: 1018 leetcode_url: https://leetcode.com/problems/binary-prefix-divisible-by-5/ categories: - arrays - math patterns: - prefix-sum function_signature: "def prefixes_div_by5(nums: list[int]) -> list[bool]:" test_cases: visible: - input: { nums: [0, 1, 1] } expected: [true, false, false] - input: { nums: [1, 1, 1] } expected: [false, false, false] - input: { nums: [1, 0, 1, 0] } expected: [false, false, true, false] hidden: - input: { nums: [0] } expected: [true] - input: { nums: [1] } expected: [false] - input: { nums: [1, 0, 1] } expected: [false, false, true] - input: { nums: [0, 0, 0, 0, 0] } expected: [true, true, true, true, true] - input: { nums: [1, 1, 0, 0, 1] } expected: [false, false, false, false, false] - input: { nums: [1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1] } expected: [false, false, false, false, true, true, true, false, false, true, true, true, true, true, true, false, false, false, false, false] description: | You are given a binary array `nums` (**0-indexed**). We define `x_i` 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]`, then `x_0 = 1`, `x_1 = 2`, and `x_2 = 5`. Return *an array of booleans* `answer` *where* `answer[i]` *is* `true` *if* `x_i` *is divisible by* `5`. constraints: | - `1 <= nums.length <= 10^5` - `nums[i]` is either `0` or `1` examples: - 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." - input: "nums = [1,1,1]" output: "[false, false, false]" explanation: "The binary numbers are 1, 11, 111; which are 1, 3, and 7 in base-10. None are divisible by 5." - input: "nums = [1,0,1,0]" output: "[false, false, true, false]" explanation: "The binary numbers are 1, 10, 101, 1010; which are 1, 2, 5, and 10 in base-10. The values 5 and 10 are divisible by 5, but 10 appears at index 3, so answer = [false, false, true, false]." explanation: intuition: | Imagine you're reading a binary number digit by digit, from left to right. Each time you read a new digit, you're essentially building up a larger number. Think of it like this: if you have the number `5` (binary `101`) and you append a `0`, you get `1010` which is `10` in decimal. Appending a digit doubles the previous number and adds the new bit. Mathematically: `new_number = old_number * 2 + new_bit`. The key insight is that we don't need to track the actual number — it could grow astronomically large (up to 2100000). Instead, we only care about **divisibility by 5**, which means we only need to track the **remainder when divided by 5**. This works because of **modular arithmetic**: if we know the remainder of `old_number % 5`, we can compute `new_number % 5` directly as `(old_number * 2 + new_bit) % 5`. The intermediate large numbers never need to be stored. approach: | We solve this using a **Running Remainder** approach: **Step 1: Initialise variables** - `current`: Set to `0` to represent the running number mod 5 - `result`: An empty list to collect our boolean answers   **Step 2: Iterate through the binary array** - For each bit in `nums`, update `current` using the formula: `current = (current * 2 + bit) % 5` - This simulates "shifting left and adding the new bit" while keeping only the remainder - Append `True` to `result` if `current == 0`, otherwise append `False`   **Step 3: Return the result** - Return the `result` list containing booleans for each prefix   This approach works because `(a * 2 + b) % 5 = ((a % 5) * 2 + b) % 5`. We maintain the invariant that `current` always holds `x_i % 5`. common_pitfalls: - title: Computing the Actual Binary Number description: | A naive approach might try to build the actual integer value at each step: ```python num = int(''.join(map(str, nums[:i+1])), 2) ``` With `nums.length <= 10^5`, the binary number can have 100,000 digits. This number is astronomically large (approximately 2100000) and will cause **memory issues** and **timeout errors**. Instead, use modular arithmetic to track only the remainder. wrong_approach: "Converting binary subarray to integer" correct_approach: "Track running remainder mod 5" - title: Forgetting to Apply Modulo After Each Step description: | If you compute `current = current * 2 + bit` without taking `% 5`, the value will still overflow (in languages with fixed-size integers) or grow unnecessarily large. Always apply `% 5` at each step: `current = (current * 2 + bit) % 5`. wrong_approach: "Accumulating without modulo" correct_approach: "Apply % 5 after each update" - title: Off-by-One in Understanding the Prefix description: | The prefix `x_i` includes `nums[0]` through `nums[i]` inclusive. Make sure you're checking divisibility at the right moment — after incorporating the ith bit, not before. key_takeaways: - "**Modular arithmetic** allows you to track divisibility without storing huge numbers" - "**Building numbers bit by bit**: `new = old * 2 + bit` is the fundamental operation for constructing binary numbers left-to-right" - "**Property preservation**: If you only care about a property (like divisibility), you may not need the full value" - "**Pattern recognition**: This technique applies to any divisibility check on incrementally built numbers" time_complexity: "O(n). We traverse the array once, performing constant-time arithmetic at each step." space_complexity: "O(n). We store the result array of length `n`. The running remainder uses O(1) extra space." solutions: - approach_name: Running Remainder is_optimal: true code: | def prefixes_div_by5(nums: list[int]) -> list[bool]: # Track the current number mod 5 current = 0 result = [] for bit in nums: # Shift left (multiply by 2) and add new bit, keep only remainder current = (current * 2 + bit) % 5 # Check if current prefix is divisible by 5 result.append(current == 0) return result explanation: | **Time Complexity:** O(n) — Single pass through the array. **Space Complexity:** O(n) — Output array of size n; O(1) auxiliary space. We use modular arithmetic to track divisibility without computing the actual (potentially huge) binary numbers. At each step, `current` holds the value of the prefix modulo 5. - approach_name: Brute Force (Convert to Integer) is_optimal: false code: | def prefixes_div_by5(nums: list[int]) -> list[bool]: result = [] for i in range(len(nums)): # Build binary string from prefix binary_str = ''.join(str(b) for b in nums[:i+1]) # Convert to integer num = int(binary_str, 2) # Check divisibility result.append(num % 5 == 0) return result explanation: | **Time Complexity:** O(n^2) — For each of n prefixes, we build a string of length up to n. **Space Complexity:** O(n) — The binary string can grow up to length n. This approach works for small inputs but fails on large arrays due to the enormous numbers involved. With `n = 10^5`, the final number has 100,000 binary digits — far too large to handle efficiently. Included to illustrate why modular arithmetic is essential.