3618. Split Array by Prime Indices
Problem Description
You are given an integer array nums
.
Your task is to split the array into two separate arrays A
and B
based on the following rule:
- Elements at prime indices in
nums
go into arrayA
- All other elements go into array
B
A prime index is an index position that is a prime number. For example:
- Index 2 is prime (goes to
A
) - Index 3 is prime (goes to
A
) - Index 0 is not prime (goes to
B
) - Index 1 is not prime (goes to
B
) - Index 4 is not prime (goes to
B
) - Index 5 is prime (goes to
A
)
After splitting the elements into the two arrays, calculate the sum of each array and return the absolute difference between these sums: |sum(A) - sum(B)|
.
If either array ends up empty, its sum is considered to be 0.
Example walkthrough:
If nums = [10, 20, 30, 40, 50, 60]
:
- Prime indices: 2, 3, 5
- Array
A
gets elements at indices 2, 3, 5:[30, 40, 60]
with sum = 130 - Array
B
gets elements at indices 0, 1, 4:[10, 20, 50]
with sum = 80 - Result:
|130 - 80| = 50
Intuition
The key insight is recognizing that we don't actually need to create two separate arrays. Instead, we can think about the problem as calculating a running difference.
When we split elements into two groups and want to find |sum(A) - sum(B)|
, we can reframe this mathematically. Each element either contributes positively to one sum or to the other. If an element goes to array A
, it increases the difference by that amount. If it goes to array B
, it decreases the difference by that amount.
So we can iterate through the array once and for each element:
- If its index is prime → add it to our running total (as if adding to
sum(A)
) - If its index is not prime → subtract it from our running total (as if adding to
sum(B)
)
This gives us sum(A) - sum(B)
directly. Taking the absolute value gives us our answer.
The elegance of this approach is that sum(x if primes[i] else -x for i, x in enumerate(nums))
computes exactly sum(A) - sum(B)
in a single pass. Each element at a prime index contributes positively, while each element at a non-prime index contributes negatively.
For determining which indices are prime, since we're dealing with array indices that could go up to 10^5
, we can efficiently precompute all primes up to this range using the Sieve of Eratosthenes algorithm. This is much faster than checking each index individually for primality, especially when dealing with multiple test cases.
Learn more about Math patterns.
Solution Approach
The solution uses the Sieve of Eratosthenes algorithm for preprocessing prime numbers, combined with a clever simulation technique to calculate the result.
Step 1: Precompute Prime Numbers
We first create a boolean array primes
of size 10^5 + 10
to mark which numbers are prime:
- Initialize all values to
True
- Mark
primes[0] = False
andprimes[1] = False
(0 and 1 are not prime) - For each number
i
from 2 tom
:- If
i
is still marked as prime, mark all its multiples as non-prime - This is done by iterating through
j = i+i, i+2i, i+3i, ...
and settingprimes[j] = False
- If
This preprocessing runs once and gives us O(1) lookup for checking if any index is prime.
Step 2: Calculate the Sum Difference
Instead of creating two separate arrays, we use a mathematical trick:
- Iterate through
nums
withenumerate()
to get both indexi
and valuex
- For each element:
- If
primes[i]
isTrue
→ addx
to the sum (element goes to array A) - If
primes[i]
isFalse
→ subtractx
from the sum (element goes to array B)
- If
This is elegantly expressed as: sum(x if primes[i] else -x for i, x in enumerate(nums))
This single expression computes sum(A) - sum(B)
directly because:
- Elements at prime indices contribute positively (
+x
) - Elements at non-prime indices contribute negatively (
-x
)
Step 3: Return Absolute Value
Since we need |sum(A) - sum(B)|
, we wrap the entire calculation in abs()
.
The time complexity is O(n) for processing the array, with O(m log log m) preprocessing for the sieve. The space complexity is O(m) for storing the prime number lookup 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 a small example with nums = [5, 8, 2, 7, 3]
.
Step 1: Identify Prime Indices Using our precomputed sieve, we check each index:
- Index 0: Not prime (0 is never prime)
- Index 1: Not prime (1 is never prime)
- Index 2: Prime ✓
- Index 3: Prime ✓
- Index 4: Not prime (4 = 2×2)
Step 2: Apply the Mathematical Trick Instead of creating two arrays, we calculate the running difference:
- Index 0 (value=5): Not prime → contribute
-5
to sum - Index 1 (value=8): Not prime → contribute
-8
to sum - Index 2 (value=2): Prime → contribute
+2
to sum - Index 3 (value=7): Prime → contribute
+7
to sum - Index 4 (value=3): Not prime → contribute
-3
to sum
Running calculation: -5 + (-8) + 2 + 7 + (-3) = -7
Step 3: Take Absolute Value
Result = |-7| = 7
Verification with Traditional Approach:
- Array A (prime indices 2, 3):
[2, 7]
→ sum = 9 - Array B (non-prime indices 0, 1, 4):
[5, 8, 3]
→ sum = 16 - Difference:
|9 - 16| = 7
✓
This demonstrates how the single-pass calculation sum(x if primes[i] else -x for i, x in enumerate(nums))
directly computes the difference without actually creating the two arrays.
Solution Implementation
1from typing import List
2
3# Sieve of Eratosthenes to precompute prime numbers up to 10^5 + 10
4MAX_SIZE = 10**5 + 10
5is_prime = [True] * MAX_SIZE
6is_prime[0] = is_prime[1] = False # 0 and 1 are not prime
7
8# Mark all multiples of prime numbers as not prime
9for i in range(2, MAX_SIZE):
10 if is_prime[i]:
11 # Mark all multiples of i as not prime
12 for j in range(i * 2, MAX_SIZE, i):
13 is_prime[j] = False
14
15
16class Solution:
17 def splitArray(self, nums: List[int]) -> int:
18 """
19 Calculate the absolute difference between sum of elements at prime indices
20 and sum of elements at non-prime indices.
21
22 Args:
23 nums: List of integers
24
25 Returns:
26 The absolute value of the difference between the two sums
27 """
28 # Sum elements at prime indices (positive) and non-prime indices (negative)
29 # Then take absolute value of the total
30 total_sum = sum(
31 nums[i] if is_prime[i] else -nums[i]
32 for i in range(len(nums))
33 )
34 return abs(total_sum)
35
1class Solution {
2 // Maximum limit for prime number generation
3 private static final int MAX_LIMIT = 100010;
4
5 // Boolean array to mark prime numbers (true = prime, false = not prime)
6 private static boolean[] isPrime = new boolean[MAX_LIMIT];
7
8 // Static initialization block to generate all prime numbers up to MAX_LIMIT
9 // using the Sieve of Eratosthenes algorithm
10 static {
11 // Initially mark all numbers as prime
12 for (int i = 0; i < MAX_LIMIT; i++) {
13 isPrime[i] = true;
14 }
15
16 // 0 and 1 are not prime numbers
17 isPrime[0] = false;
18 isPrime[1] = false;
19
20 // Sieve of Eratosthenes algorithm
21 for (int i = 2; i < MAX_LIMIT; i++) {
22 if (isPrime[i]) {
23 // Mark all multiples of i as non-prime
24 for (int multiple = i + i; multiple < MAX_LIMIT; multiple += i) {
25 isPrime[multiple] = false;
26 }
27 }
28 }
29 }
30
31 /**
32 * Splits the array based on prime indices.
33 * Adds values at prime indices and subtracts values at non-prime indices,
34 * then returns the absolute value of the sum.
35 *
36 * @param nums the input array to process
37 * @return the absolute value of the weighted sum
38 */
39 public long splitArray(int[] nums) {
40 long weightedSum = 0;
41
42 // Iterate through the array
43 for (int index = 0; index < nums.length; index++) {
44 // Add value if index is prime, subtract if not
45 if (isPrime[index]) {
46 weightedSum += nums[index];
47 } else {
48 weightedSum -= nums[index];
49 }
50 }
51
52 // Return the absolute value of the weighted sum
53 return Math.abs(weightedSum);
54 }
55}
56
1// Constants
2const int MAX_SIZE = 1e5 + 10;
3
4// Global array to store prime number flags
5bool isPrime[MAX_SIZE];
6
7// Initialize prime number sieve using Sieve of Eratosthenes
8// This lambda function executes immediately to initialize the prime array
9auto initialize = [] {
10 // Initially mark all numbers as prime
11 memset(isPrime, true, sizeof(isPrime));
12
13 // 0 and 1 are not prime numbers
14 isPrime[0] = isPrime[1] = false;
15
16 // Sieve of Eratosthenes algorithm
17 for (int i = 2; i < MAX_SIZE; ++i) {
18 if (isPrime[i]) {
19 // Mark all multiples of i as non-prime
20 for (int j = i + i; j < MAX_SIZE; j += i) {
21 isPrime[j] = false;
22 }
23 }
24 }
25 return 0;
26}();
27
28class Solution {
29public:
30 /**
31 * Splits array based on prime indices and calculates the absolute difference
32 *
33 * The algorithm adds values at prime indices and subtracts values at non-prime indices,
34 * then returns the absolute value of the result.
35 *
36 * @param nums Input vector of integers
37 * @return Absolute value of the sum after applying prime index rules
38 */
39 long long splitArray(vector<int>& nums) {
40 long long result = 0;
41
42 // Iterate through each element in the array
43 for (int i = 0; i < nums.size(); ++i) {
44 // Add value if index is prime, subtract if index is not prime
45 result += isPrime[i] ? nums[i] : -nums[i];
46 }
47
48 // Return the absolute value of the calculated sum
49 return abs(result);
50 }
51};
52
1// Maximum value for prime sieve
2const MAX_VALUE = 100000 + 10;
3
4// Boolean array to mark prime numbers (true = prime, false = not prime)
5const isPrime: boolean[] = Array(MAX_VALUE).fill(true);
6
7// Initialize the prime sieve using Sieve of Eratosthenes algorithm
8const initializePrimeSieve = (() => {
9 // 0 and 1 are not prime numbers
10 isPrime[0] = isPrime[1] = false;
11
12 // Sieve of Eratosthenes: mark all multiples of prime numbers as non-prime
13 for (let i = 2; i < MAX_VALUE; i++) {
14 if (isPrime[i]) {
15 // Mark all multiples of i as non-prime
16 for (let j = i + i; j < MAX_VALUE; j += i) {
17 isPrime[j] = false;
18 }
19 }
20 }
21})();
22
23/**
24 * Splits an array by assigning positive or negative signs based on prime indices
25 * @param nums - The input array of numbers
26 * @returns The absolute value of the sum after applying signs based on prime indices
27 */
28function splitArray(nums: number[]): number {
29 let sum = 0;
30
31 // Add positive value if index is prime, negative value if index is not prime
32 for (let i = 0; i < nums.length; i++) {
33 sum += isPrime[i] ? nums[i] : -nums[i];
34 }
35
36 // Return the absolute value of the final sum
37 return Math.abs(sum);
38}
39
Time and Space Complexity
The code consists of two parts: preprocessing to generate prime numbers using the Sieve of Eratosthenes, and the main solution.
Preprocessing (Sieve of Eratosthenes):
- Time Complexity:
O(m log log m)
wherem = 10^5 + 10
- Space Complexity:
O(m)
for theprimes
boolean array
Main Solution (splitArray
method):
- Time Complexity:
O(n)
wheren
is the length of the input arraynums
. The solution iterates through the array once, performing constant-time operations (checking prime status and arithmetic operations) for each element. - Space Complexity:
O(1)
excluding the preprocessedprimes
array. The generator expression computes the sum on-the-fly without creating additional data structures proportional to the input size.
Following the reference answer's convention of ignoring preprocessing time and space:
- Time Complexity:
O(n)
wheren
is the length of the arraynums
- Space Complexity:
O(1)
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Index Out of Bounds for Prime Check
The most critical pitfall is assuming the input array size won't exceed the precomputed prime table size. If len(nums)
exceeds MAX_SIZE
, the code will crash with an IndexError
when checking is_prime[i]
.
Example scenario: If nums
has 100,011 elements, accessing is_prime[100010]
will fail since our table only goes up to 100,010.
Solution:
class Solution:
def splitArray(self, nums: List[int]) -> int:
# Dynamically check if we need to extend the prime table
max_index = len(nums) - 1
if max_index >= len(is_prime):
# Either extend the sieve or handle inline
return self._handle_large_input(nums)
total_sum = sum(
nums[i] if is_prime[i] else -nums[i]
for i in range(len(nums))
)
return abs(total_sum)
2. Global State Mutation Risk
Using a global is_prime
array outside the class can lead to issues in concurrent environments or when the array needs to be modified. If another part of the code accidentally modifies is_prime
, all subsequent calls will produce incorrect results.
Solution: Encapsulate the prime computation within the class or use a class variable:
class Solution:
def __init__(self):
self.is_prime = self._compute_primes(10**5 + 10)
def _compute_primes(self, limit):
primes = [True] * limit
primes[0] = primes[1] = False
for i in range(2, int(limit**0.5) + 1):
if primes[i]:
for j in range(i*i, limit, i):
primes[j] = False
return primes
3. Integer Overflow in Large Sums
While Python handles arbitrary precision integers automatically, in other languages or constrained environments, the sum calculation might overflow for very large arrays with large values.
Solution: Consider using modular arithmetic if required by the problem constraints, or explicitly handle large number scenarios:
def splitArray(self, nums: List[int]) -> int:
sum_a = sum_b = 0
for i, val in enumerate(nums):
if is_prime[i]:
sum_a += val
else:
sum_b += val
# Check for potential overflow scenarios if needed
return abs(sum_a - sum_b)
4. Inefficient Sieve Implementation
The current sieve marks all multiples starting from i * 2
. This is less efficient than starting from i * i
since all smaller multiples would have already been marked by smaller primes.
Solution: Optimize the sieve:
for i in range(2, int(MAX_SIZE**0.5) + 1):
if is_prime[i]:
# Start from i*i instead of i*2
for j in range(i * i, MAX_SIZE, i):
is_prime[j] = False
What's the relationship between a tree and a graph?
Recommended Readings
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
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 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
Want a Structured Path to Master System Design Too? Don’t Miss This!