2453. Destroy Sequential Targets
Problem Description
You have an array nums of positive integers representing targets positioned on a number line, and an integer space.
You have a machine that can destroy targets following a specific pattern. When you seed the machine with a value nums[i], it destroys all targets whose values can be expressed as nums[i] + c * space, where c is any non-negative integer (0, 1, 2, 3, ...).
For example, if you seed the machine with value 3 and space = 4, the machine will destroy targets at positions: 3 (c=0), 7 (c=1), 11 (c=2), 15 (c=3), and so on.
Your goal is to find the minimum value from nums that, when used as the seed, allows the machine to destroy the maximum possible number of targets from the array.
The key insight is that targets that have the same remainder when divided by space form a group that can be destroyed by the same seed. For instance, with space = 4, targets at positions 3, 7, 11, 15 all have remainder 3 when divided by 4, so they can all be destroyed by seeding with 3.
The solution counts how many targets belong to each remainder group using modulo operation (v % space). Then it finds the group with the most targets and returns the smallest target value from that group as the answer.
Intuition
The key observation is understanding what values the machine can destroy when seeded with a particular number. If we seed with value x, the machine destroys targets at positions: x, x + space, x + 2*space, x + 3*space, and so on.
Notice that all these destroyed positions follow a pattern - they all have the same remainder when divided by space. Specifically, they all have remainder x % space. This is because:
x % space = x % space(obviously)(x + space) % space = x % space(x + 2*space) % space = x % space- And so on...
 
This means we can group all targets by their remainder when divided by space. Targets in the same group can potentially be destroyed by the same seed value.
For example, with space = 5:
- Targets 3, 8, 13, 18 all have remainder 3
 - Targets 2, 7, 12, 17 all have remainder 2
 
If we seed with 3, we can destroy all targets with remainder 3. If we seed with 2, we can destroy all targets with remainder 2.
Since we want to maximize the number of destroyed targets, we should find the remainder group that contains the most targets. Among all targets in that group, we choose the smallest value as our seed (since the problem asks for the minimum seed value that achieves maximum destruction).
This leads us to the solution: count targets by their value % space, find the most frequent remainder, and return the smallest target value with that remainder.
Solution Approach
The implementation uses a hash table to count the frequency of each remainder group, then finds the optimal seed value in a single pass through the array.
Step 1: Count Remainder Frequencies
We use Python's Counter to create a hash table cnt that maps each remainder to its frequency:
cnt = Counter(v % space for v in nums)
This line computes v % space for each value v in nums and counts how many times each remainder appears.
Step 2: Find the Optimal Seed
We initialize two variables:
ans: stores the minimum seed value that destroys the maximum targetsmx: tracks the maximum number of targets that can be destroyed
We then iterate through each value v in nums:
for v in nums: t = cnt[v % space] if t > mx or (t == mx and v < ans): ans = v mx = t
For each target value v:
- We look up how many targets have the same remainder as 
vusingcnt[v % space] - If this count 
tis greater than our current maximummx, we update bothansandmx - If the count equals the maximum but 
vis smaller than the current answer, we updateansto get the minimum seed value 
Why This Works:
- By checking 
cnt[v % space], we know exactly how many targets from the array can be destroyed if we seed withv - The condition 
t > mxensures we always choose the seed that destroys the most targets - The condition 
(t == mx and v < ans)handles ties by selecting the smaller seed value - Since we iterate through the original array, we're guaranteed to find the actual minimum value from each remainder group
 
The time complexity is O(n) where n is the length of nums, and the space complexity is O(min(n, space)) for the hash table.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through an example with nums = [3, 7, 8, 1, 1, 5] and space = 2.
Step 1: Group targets by remainder
First, we calculate v % space for each value:
- 3 % 2 = 1
 - 7 % 2 = 1
 - 8 % 2 = 0
 - 1 % 2 = 1
 - 1 % 2 = 1
 - 5 % 2 = 1
 
So our remainder groups are:
- Remainder 0: [8] (1 target)
 - Remainder 1: [3, 7, 1, 1, 5] (5 targets)
 
Using Counter, we get cnt = {1: 5, 0: 1}.
Step 2: Find the minimum seed that destroys maximum targets
Initialize ans = float('inf') and mx = 0.
Now iterate through each value in nums:
- 
v = 3:
- Remainder: 3 % 2 = 1
 - Count: cnt[1] = 5
 - Since 5 > mx (0), update: ans = 3, mx = 5
 
 - 
v = 7:
- Remainder: 7 % 2 = 1
 - Count: cnt[1] = 5
 - Since 5 = mx (5) but 7 > ans (3), no update
 
 - 
v = 8:
- Remainder: 8 % 2 = 0
 - Count: cnt[0] = 1
 - Since 1 < mx (5), no update
 
 - 
v = 1 (first occurrence):
- Remainder: 1 % 2 = 1
 - Count: cnt[1] = 5
 - Since 5 = mx (5) and 1 < ans (3), update: ans = 1
 
 - 
v = 1 (second occurrence):
- Remainder: 1 % 2 = 1
 - Count: cnt[1] = 5
 - Since 5 = mx (5) but 1 = ans (1), no update
 
 - 
v = 5:
- Remainder: 5 % 2 = 1
 - Count: cnt[1] = 5
 - Since 5 = mx (5) but 5 > ans (1), no update
 
 
Result: The answer is 1.
Verification: If we seed the machine with 1:
- The machine destroys positions: 1, 1+2=3, 1+4=5, 1+6=7, 1+8=9, ...
 - From our array [3, 7, 8, 1, 1, 5], it destroys: 1, 1, 3, 5, 7 (5 targets)
 - This is indeed the maximum possible, and 1 is the minimum seed that achieves it.
 
Solution Implementation
1from typing import List
2from collections import Counter
3
4class Solution:
5    def destroyTargets(self, nums: List[int], space: int) -> int:
6        # Count how many numbers have the same remainder when divided by space
7        # Numbers with the same remainder can be destroyed together by the same seed
8        remainder_count = Counter(num % space for num in nums)
9      
10        # Initialize variables to track the best answer
11        best_seed = 0  # The seed value that destroys the most targets
12        max_targets = 0  # Maximum number of targets that can be destroyed
13      
14        # Iterate through each number as a potential seed
15        for num in nums:
16            # Get the count of numbers that have the same remainder as current number
17            # These are all the targets that would be destroyed if we use this number as seed
18            targets_destroyed = remainder_count[num % space]
19          
20            # Update the best seed if:
21            # 1. Current number destroys more targets than previous best, OR
22            # 2. Current number destroys same targets but has smaller value (tiebreaker)
23            if targets_destroyed > max_targets or (targets_destroyed == max_targets and num < best_seed):
24                best_seed = num
25                max_targets = targets_destroyed
26      
27        return best_seed
281class Solution {
2    public int destroyTargets(int[] nums, int space) {
3        // Map to store frequency of each remainder when divided by space
4        // Key: remainder (v % space), Value: count of numbers with this remainder
5        Map<Integer, Integer> remainderFrequency = new HashMap<>();
6      
7        // First pass: Count frequency of each remainder
8        for (int num : nums) {
9            int remainder = num % space;
10            remainderFrequency.put(remainder, remainderFrequency.getOrDefault(remainder, 0) + 1);
11        }
12      
13        // Initialize result variables
14        int selectedTarget = 0;  // The seed value that can destroy maximum targets
15        int maxDestroyable = 0;  // Maximum number of targets that can be destroyed
16      
17        // Second pass: Find the seed with maximum destroyable targets
18        // If tie, choose the smaller seed value
19        for (int num : nums) {
20            int destroyableCount = remainderFrequency.get(num % space);
21          
22            // Update if current number can destroy more targets
23            // OR if it destroys same number of targets but has smaller value
24            if (destroyableCount > maxDestroyable || 
25                (destroyableCount == maxDestroyable && num < selectedTarget)) {
26                selectedTarget = num;
27                maxDestroyable = destroyableCount;
28            }
29        }
30      
31        return selectedTarget;
32    }
33}
341class Solution {
2public:
3    int destroyTargets(vector<int>& nums, int space) {
4        // Map to store count of numbers for each remainder when divided by space
5        // Key: remainder (v % space), Value: count of numbers with this remainder
6        unordered_map<int, int> remainderCount;
7      
8        // Count how many numbers have each remainder value
9        // Numbers with same remainder can be destroyed together as a sequence
10        for (int num : nums) {
11            remainderCount[num % space]++;
12        }
13      
14        // Find the seed that can destroy maximum targets
15        int seedValue = 0;  // The seed value to return
16        int maxTargets = 0; // Maximum number of targets that can be destroyed
17      
18        // Check each number as a potential seed
19        for (int num : nums) {
20            int targetsDestroyed = remainderCount[num % space];
21          
22            // Update seed if:
23            // 1. Current number can destroy more targets, OR
24            // 2. Can destroy same number of targets but has smaller value
25            if (targetsDestroyed > maxTargets || 
26                (targetsDestroyed == maxTargets && num < seedValue)) {
27                seedValue = num;
28                maxTargets = targetsDestroyed;
29            }
30        }
31      
32        return seedValue;
33    }
34};
351function destroyTargets(nums: number[], space: number): number {
2    // Map to store count of numbers for each remainder when divided by space
3    // Key: remainder (v % space), Value: count of numbers with this remainder
4    const remainderCount: Map<number, number> = new Map();
5  
6    // Count how many numbers have each remainder value
7    // Numbers with same remainder can be destroyed together as a sequence
8    for (const num of nums) {
9        const remainder = num % space;
10        remainderCount.set(remainder, (remainderCount.get(remainder) || 0) + 1);
11    }
12  
13    // Find the seed that can destroy maximum targets
14    let seedValue: number = 0;  // The seed value to return
15    let maxTargets: number = 0;  // Maximum number of targets that can be destroyed
16  
17    // Check each number as a potential seed
18    for (const num of nums) {
19        const targetsDestroyed = remainderCount.get(num % space) || 0;
20      
21        // Update seed if:
22        // 1. Current number can destroy more targets, OR
23        // 2. Can destroy same number of targets but has smaller value
24        if (targetsDestroyed > maxTargets || 
25            (targetsDestroyed === maxTargets && num < seedValue)) {
26            seedValue = num;
27            maxTargets = targetsDestroyed;
28        }
29    }
30  
31    return seedValue;
32}
33Time and Space Complexity
The time complexity is O(n), where n is the length of the array nums. This is because:
- Creating the Counter object requires iterating through all 
nelements once to computev % spacefor each element:O(n) - The second loop iterates through all 
nelements innumsto find the answer:O(n) - Inside the second loop, accessing the count value 
cnt[v % space]isO(1)on average for dictionary lookup - Overall: 
O(n) + O(n) = O(n) 
The space complexity is O(n). This is because:
- The Counter object 
cntstores at mostnunique values (in the worst case where allv % spacevalues are different) - In the best case, it could store as few as 1 unique value, but worst-case analysis gives us 
O(n) - The variables 
ans,mx, andtuse constant space:O(1) - Overall: 
O(n) + O(1) = O(n) 
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrectly Finding the Minimum Value in a Remainder Group
A common mistake is to assume that after identifying the remainder group with the maximum count, you need to find ALL values in that group and then select the minimum. This leads to inefficient two-pass solutions:
Incorrect Approach:
# First pass: find the remainder with max count
max_remainder = max(remainder_count, key=remainder_count.get)
max_count = remainder_count[max_remainder]
# Second pass: find all values with that remainder and return minimum
candidates = [num for num in nums if num % space == max_remainder]
return min(candidates)
Problems with this approach:
- Requires two full passes through the array
 - Doesn't handle ties correctly (when multiple remainders have the same maximum count)
 - More complex and error-prone
 
Correct Solution: The provided solution elegantly handles this in a single pass by checking each number as we iterate and updating the answer only when we find a better candidate (more targets destroyed) or an equal candidate with a smaller value.
2. Not Handling Ties Properly
When multiple remainder groups have the same maximum count, you must return the smallest seed value across ALL these groups, not just the smallest value from the first group you encounter.
Incorrect Logic:
if targets_destroyed > max_targets: # Missing the tie-breaking condition best_seed = num max_targets = targets_destroyed
This would return an arbitrary seed when there are ties, not necessarily the minimum one.
Correct Logic:
if targets_destroyed > max_targets or (targets_destroyed == max_targets and num < best_seed): best_seed = num max_targets = targets_destroyed
3. Using the Remainder as the Answer Instead of the Actual Value
A conceptual error is returning the remainder that has the maximum count instead of an actual value from the array.
Wrong:
max_remainder = max(remainder_count, key=remainder_count.get)
return max_remainder  # This returns a remainder (0 to space-1), not a value from nums!
Right:
The solution correctly returns an actual value from nums that serves as the seed, not just the remainder value.
4. Initializing Variables Incorrectly
Setting best_seed = float('inf') or best_seed = nums[0] without proper initialization of max_targets can lead to incorrect results.
Problematic Initialization:
best_seed = float('inf')
max_targets = 0
With this initialization, if all remainder groups have count 1, the condition num < best_seed would always be true for the first element, but we'd never update max_targets properly.
Better Initialization:
best_seed = 0 max_targets = 0
Since we're guaranteed to update both values together when we find our first valid candidate, the initial value of best_seed doesn't matter as long as max_targets starts at 0.
Which of the following is a good use case for backtracking?
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion If you prefer videos here's a video that explains recursion in a fun and easy way Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask him
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!