3556. Sum of Largest Prime Substrings
Problem Description
Given a string s
, you need to find all possible substrings that can form prime numbers and calculate the sum of the 3 largest unique prime numbers.
A substring is any contiguous sequence of characters within the string. For example, if s = "123"
, the substrings are: "1"
, "2"
, "3"
, "12"
, "23"
, "123"
.
When converting a substring to a number:
- Leading zeros are ignored (e.g.,
"012"
becomes12
) - Each unique prime number is counted only once, even if it appears multiple times through different substrings
The task requires you to:
- Extract all possible substrings from the given string
- Convert each substring to an integer and check if it's a prime number
- Collect all unique prime numbers found
- Return the sum of the 3 largest unique primes
Special cases:
- If there are fewer than 3 unique prime numbers, return the sum of all available primes
- If no prime numbers can be formed from any substring, return
0
For example, if the string contains substrings that form the primes [2, 3, 5, 7, 11]
, the answer would be 5 + 7 + 11 = 23
(sum of the 3 largest).
Intuition
The key insight is that we need to explore all possible ways to extract numbers from the string, which naturally leads us to think about substrings. Since we want to find prime numbers, we need to check every possible substring that could represent a valid number.
The brute force approach makes sense here because:
- We must examine all substrings anyway - there's no way to know which substrings form primes without checking them
- A substring starting at position
i
and ending at positionj
represents a potential number
To build each number from a substring, we can use the digit-by-digit construction method: starting from position i
, we keep extending to the right, building the number by multiplying the current value by 10 and adding the new digit. For example, to build 123
from string "123"
: first we have 1
, then 1 * 10 + 2 = 12
, then 12 * 10 + 3 = 123
.
Since we only care about unique primes (each prime counted once), a set data structure is perfect for storing the primes we find - it automatically handles duplicates for us.
The primality check is straightforward: for each number x
, we check if any number from 2
to sqrt(x)
divides it evenly. If none do, it's prime. We only need to check up to sqrt(x)
because if x
has a divisor greater than sqrt(x)
, it must also have a corresponding divisor less than sqrt(x)
.
Finally, to get the 3 largest primes, we can sort the set and take the last 3 elements. Python's slice notation [-3:]
elegantly handles the case where we have fewer than 3 primes - it just returns whatever elements are available.
Solution Approach
The solution uses an enumeration approach combined with a hash table (set) to find all unique prime numbers from the string's substrings.
Step 1: Initialize Data Structures
- Create an empty set
st
to store unique prime numbers - Get the length
n
of the input string
Step 2: Enumerate All Substrings
- Use two nested loops to generate all possible substrings:
- Outer loop:
i
from0
ton-1
(starting position) - Inner loop:
j
fromi
ton-1
(ending position)
- Outer loop:
- For each starting position
i
, initializex = 0
to build the number
Step 3: Build Numbers from Substrings
- For each character at position
j
, convert it to a digit and append to the current number:x = x * 10 + int(s[j])
- This builds the number digit by digit. For example, with substring
"123"
:- First iteration:
x = 0 * 10 + 1 = 1
- Second iteration:
x = 1 * 10 + 2 = 12
- Third iteration:
x = 12 * 10 + 3 = 123
- First iteration:
Step 4: Check for Primality
- Implement the
is_prime
function:- Numbers less than 2 are not prime
- For numbers ≥ 2, check if any number from
2
tosqrt(x)
dividesx
- Use
all(x % i for i in range(2, int(sqrt(x)) + 1))
to verify no divisors exist
- If
x
is prime, add it to the setst
Step 5: Calculate the Result
- Sort the set of primes in ascending order:
sorted(st)
- Take the last 3 elements using slice notation:
[-3:]
- Return the sum of these elements
The beauty of this approach is that:
- The set automatically handles duplicate primes
- Python's slice notation
[-3:]
gracefully handles cases with fewer than 3 primes - The sum of an empty list returns
0
, handling the case with no primes
Time Complexity: O(n² × √m)
where n
is the string length and m
is the maximum number value
Space Complexity: O(k)
where k
is the number of unique primes found
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 the solution with the string s = "2357"
.
Step 1: Initialize
- Create empty set
st = {}
- String length
n = 4
Step 2-4: Generate substrings and check for primes
Starting from position i = 0
(character '2'):
j = 0
: substring "2",x = 0 * 10 + 2 = 2
- Is 2 prime? Yes → Add to set:
st = {2}
- Is 2 prime? Yes → Add to set:
j = 1
: substring "23",x = 2 * 10 + 3 = 23
- Is 23 prime? Check divisors from 2 to 4 (√23 ≈ 4.8)
- 23 % 2 = 1, 23 % 3 = 2, 23 % 4 = 3 → Yes, prime →
st = {2, 23}
j = 2
: substring "235",x = 23 * 10 + 5 = 235
- Is 235 prime? 235 % 5 = 0 → No, not prime
j = 3
: substring "2357",x = 235 * 10 + 7 = 2357
- Is 2357 prime? Check up to √2357 ≈ 48
- 2357 is actually prime →
st = {2, 23, 2357}
Starting from position i = 1
(character '3'):
j = 1
: substring "3",x = 0 * 10 + 3 = 3
- Is 3 prime? Yes →
st = {2, 3, 23, 2357}
- Is 3 prime? Yes →
j = 2
: substring "35",x = 3 * 10 + 5 = 35
- Is 35 prime? 35 % 5 = 0 → No
j = 3
: substring "357",x = 35 * 10 + 7 = 357
- Is 357 prime? 357 % 3 = 0 → No
Starting from position i = 2
(character '5'):
j = 2
: substring "5",x = 0 * 10 + 5 = 5
- Is 5 prime? Yes →
st = {2, 3, 5, 23, 2357}
- Is 5 prime? Yes →
j = 3
: substring "57",x = 5 * 10 + 7 = 57
- Is 57 prime? 57 % 3 = 0 → No
Starting from position i = 3
(character '7'):
j = 3
: substring "7",x = 0 * 10 + 7 = 7
- Is 7 prime? Yes →
st = {2, 3, 5, 7, 23, 2357}
- Is 7 prime? Yes →
Step 5: Find sum of 3 largest primes
- Final set of primes:
{2, 3, 5, 7, 23, 2357}
- Sort the set:
[2, 3, 5, 7, 23, 2357]
- Take last 3 elements:
[7, 23, 2357]
- Return sum:
7 + 23 + 2357 = 2387
The solution efficiently handles all edge cases:
- If we had found only 2 primes like
{3, 7}
, the slice[-3:]
would return[3, 7]
and sum to 10 - If no primes were found, the empty list would sum to 0
Solution Implementation
1from math import sqrt
2
3class Solution:
4 def sumOfLargestPrimes(self, s: str) -> int:
5 """
6 Find all prime numbers that can be formed from contiguous substrings of s,
7 and return the sum of the 3 largest unique primes.
8
9 Args:
10 s: A string containing digits
11
12 Returns:
13 Sum of the 3 largest unique prime numbers found in substrings
14 """
15
16 def is_prime(num: int) -> bool:
17 """
18 Check if a number is prime.
19
20 Args:
21 num: Integer to check for primality
22
23 Returns:
24 True if num is prime, False otherwise
25 """
26 if num < 2:
27 return False
28
29 # Check divisibility from 2 to sqrt(num)
30 for i in range(2, int(sqrt(num)) + 1):
31 if num % i == 0:
32 return False
33 return True
34
35 # Set to store unique prime numbers found
36 prime_set = set()
37 n = len(s)
38
39 # Generate all possible contiguous substrings
40 for start_idx in range(n):
41 current_num = 0
42
43 # Build numbers starting from start_idx
44 for end_idx in range(start_idx, n):
45 # Append the current digit to form a new number
46 current_num = current_num * 10 + int(s[end_idx])
47
48 # If the formed number is prime, add to set
49 if is_prime(current_num):
50 prime_set.add(current_num)
51
52 # Sort the primes and return sum of the 3 largest
53 # If less than 3 primes exist, sum all available
54 sorted_primes = sorted(prime_set)
55 return sum(sorted_primes[-3:])
56
1class Solution {
2 /**
3 * Finds all unique prime numbers that can be formed from substrings of the input string,
4 * and returns the sum of the largest 3 primes (or fewer if less than 3 primes exist).
5 *
6 * @param s Input string containing digits
7 * @return Sum of up to 3 largest prime numbers found
8 */
9 public long sumOfLargestPrimes(String s) {
10 // Set to store unique prime numbers found
11 Set<Long> primeSet = new HashSet<>();
12 int stringLength = s.length();
13
14 // Iterate through all possible substrings
15 for (int startIndex = 0; startIndex < stringLength; startIndex++) {
16 long currentNumber = 0;
17
18 // Build numbers from substrings starting at startIndex
19 for (int endIndex = startIndex; endIndex < stringLength; endIndex++) {
20 // Append the next digit to form a new number
21 currentNumber = currentNumber * 10 + (s.charAt(endIndex) - '0');
22
23 // Check if the formed number is prime and add to set if true
24 if (isPrime(currentNumber)) {
25 primeSet.add(currentNumber);
26 }
27 }
28 }
29
30 // Convert set to list for sorting
31 List<Long> sortedPrimes = new ArrayList<>(primeSet);
32 Collections.sort(sortedPrimes);
33
34 // Calculate sum of up to 3 largest primes
35 long sum = 0;
36 int startPosition = Math.max(0, sortedPrimes.size() - 3);
37
38 for (int index = startPosition; index < sortedPrimes.size(); index++) {
39 sum += sortedPrimes.get(index);
40 }
41
42 return sum;
43 }
44
45 /**
46 * Checks if a given number is prime.
47 *
48 * @param number The number to check for primality
49 * @return true if the number is prime, false otherwise
50 */
51 private boolean isPrime(long number) {
52 // Numbers less than 2 are not prime
53 if (number < 2) {
54 return false;
55 }
56
57 // Check for divisibility up to square root of the number
58 for (long divisor = 2; divisor * divisor <= number; divisor++) {
59 if (number % divisor == 0) {
60 return false;
61 }
62 }
63
64 return true;
65 }
66}
67
1class Solution {
2public:
3 long long sumOfLargestPrimes(string s) {
4 // Set to store unique prime numbers found in string
5 unordered_set<long long> primeSet;
6 int stringLength = s.size();
7
8 // Generate all possible substrings and check if they form prime numbers
9 for (int startIdx = 0; startIdx < stringLength; ++startIdx) {
10 long long currentNumber = 0;
11
12 // Build numbers starting from startIdx
13 for (int endIdx = startIdx; endIdx < stringLength; ++endIdx) {
14 // Append current digit to form a new number
15 currentNumber = currentNumber * 10 + (s[endIdx] - '0');
16
17 // If the current number is prime, add it to the set
18 if (isPrime(currentNumber)) {
19 primeSet.insert(currentNumber);
20 }
21 }
22 }
23
24 // Convert set to vector for sorting
25 vector<long long> sortedPrimes(primeSet.begin(), primeSet.end());
26 sort(sortedPrimes.begin(), sortedPrimes.end());
27
28 // Sum up the three largest prime numbers (or fewer if less than 3 exist)
29 long long result = 0;
30 int count = 0;
31
32 for (int i = static_cast<int>(sortedPrimes.size()) - 1; i >= 0 && count < 3; --i, ++count) {
33 result += sortedPrimes[i];
34 }
35
36 return result;
37 }
38
39private:
40 // Helper function to check if a number is prime
41 bool isPrime(long long num) {
42 // Numbers less than 2 are not prime
43 if (num < 2) {
44 return false;
45 }
46
47 // Check divisibility up to sqrt(num)
48 for (long long divisor = 2; divisor * divisor <= num; ++divisor) {
49 if (num % divisor == 0) {
50 return false;
51 }
52 }
53
54 return true;
55 }
56};
57
1/**
2 * Finds all prime numbers that can be formed from substrings of the input string
3 * and returns the sum of the three largest primes
4 * @param s - Input string containing digits
5 * @returns Sum of the three largest prime numbers found
6 */
7function sumOfLargestPrimes(s: string): number {
8 // Set to store unique prime numbers found
9 const primeSet = new Set<number>();
10 const stringLength = s.length;
11
12 // Iterate through all possible starting positions
13 for (let startIndex = 0; startIndex < stringLength; startIndex++) {
14 let currentNumber = 0;
15
16 // Build numbers from substrings starting at startIndex
17 for (let endIndex = startIndex; endIndex < stringLength; endIndex++) {
18 // Append the next digit to form a new number
19 currentNumber = currentNumber * 10 + Number(s[endIndex]);
20
21 // Check if the formed number is prime and add to set
22 if (isPrime(currentNumber)) {
23 primeSet.add(currentNumber);
24 }
25 }
26 }
27
28 // Convert set to array and sort in ascending order
29 const sortedPrimes = Array.from(primeSet).sort((a, b) => a - b);
30
31 // Get the last three elements (largest primes)
32 const largestThreePrimes = sortedPrimes.slice(-3);
33
34 // Calculate and return the sum of the largest three primes
35 return largestThreePrimes.reduce((sum, value) => sum + value, 0);
36}
37
38/**
39 * Checks if a number is prime
40 * @param x - Number to check for primality
41 * @returns true if the number is prime, false otherwise
42 */
43function isPrime(x: number): boolean {
44 // Numbers less than 2 are not prime
45 if (x < 2) return false;
46
47 // Check divisibility up to square root of x
48 for (let divisor = 2; divisor * divisor <= x; divisor++) {
49 if (x % divisor === 0) {
50 return false;
51 }
52 }
53
54 return true;
55}
56
Time and Space Complexity
Time Complexity: O(n² × √M)
The algorithm uses nested loops to generate all possible substrings:
- The outer loop runs
n
times (wheren
is the length of the string) - The inner loop runs up to
n
times for each iteration of the outer loop - This gives us
O(n²)
substring generations
For each substring generated, we check if it's prime using the is_prime
function:
- The
is_prime
function iterates from 2 to√x
wherex
is the numeric value of the substring - The maximum value of a substring is
M
(the largest possible numeric value from the string) - Therefore, the prime check takes
O(√M)
time
Combining these operations: O(n²)
substrings × O(√M)
prime check = O(n² × √M)
Additional operations like adding to set (O(1)
), sorting (O(k log k)
where k ≤ n²
), and summing (O(1)
) don't dominate the complexity.
Space Complexity: O(n²)
The space is primarily consumed by:
- The set
st
which stores all prime numbers found from substrings - In the worst case, all
O(n²)
possible substrings could be prime numbers - Each number in the set takes
O(1)
space - Therefore, the total space complexity is
O(n²)
The temporary variables and the sorted list creation don't exceed this bound.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Integer Overflow with Large Numbers
Pitfall: When processing long strings, the accumulated number current_num
can become extremely large, potentially exceeding typical integer limits in some languages or causing performance issues during primality checking.
For example, a string like "9876543210123456789"
would generate massive numbers that are computationally expensive to check for primality.
Solution:
- Add an upper bound check to skip numbers above a reasonable threshold
- Consider limiting the maximum substring length to prevent overflow
MAX_PRIME_CHECK = 10**9 # Set reasonable upper bound
for start_idx in range(n):
current_num = 0
for end_idx in range(start_idx, n):
current_num = current_num * 10 + int(s[end_idx])
# Skip if number exceeds reasonable bounds
if current_num > MAX_PRIME_CHECK:
break
if is_prime(current_num):
prime_set.add(current_num)
2. Inefficient Primality Testing for Large Numbers
Pitfall: The basic primality test iterates through all numbers from 2 to √n, which becomes slow for large numbers. If the string generates many large numbers, the overall performance degrades significantly.
Solution:
- Implement optimized primality testing that checks for 2 and odd numbers only
- Use memoization to cache previously computed prime checks
# Optimized primality check
def is_prime(num: int) -> bool:
if num < 2:
return False
if num == 2:
return True
if num % 2 == 0:
return False
# Only check odd divisors
for i in range(3, int(sqrt(num)) + 1, 2):
if num % i == 0:
return False
return True
# Or use memoization
prime_cache = {}
def is_prime_cached(num: int) -> bool:
if num in prime_cache:
return prime_cache[num]
result = is_prime(num)
prime_cache[num] = result
return result
3. Handling Leading Zeros Incorrectly
Pitfall: The current implementation treats "01", "001", etc. as distinct from "1" during substring generation, but they all represent the same number. This isn't necessarily wrong since the set handles duplicates, but it causes unnecessary primality checks.
Solution:
- Skip substrings that start with '0' (except for the single digit '0' itself)
for start_idx in range(n):
# Skip if starting with '0' and not a single '0'
if s[start_idx] == '0':
# Only check single '0', skip multi-digit numbers starting with 0
if is_prime(0): # Will return False
prime_set.add(0)
continue
current_num = 0
for end_idx in range(start_idx, n):
current_num = current_num * 10 + int(s[end_idx])
if is_prime(current_num):
prime_set.add(current_num)
4. Memory Issues with Large Prime Sets
Pitfall: For strings with many digits, the set of unique primes could grow very large, consuming significant memory.
Solution:
- Maintain only the top 3 primes using a min-heap instead of storing all primes
import heapq
def sumOfLargestPrimes(self, s: str) -> int:
min_heap = [] # Store only top 3 primes
seen_primes = set() # Track which primes we've seen
for start_idx in range(n):
current_num = 0
for end_idx in range(start_idx, n):
current_num = current_num * 10 + int(s[end_idx])
if current_num not in seen_primes and is_prime(current_num):
seen_primes.add(current_num)
if len(min_heap) < 3:
heapq.heappush(min_heap, current_num)
elif current_num > min_heap[0]:
heapq.heapreplace(min_heap, current_num)
return sum(min_heap)
What's the output of running the following function using the following tree as input?
1def serialize(root):
2 res = []
3 def dfs(root):
4 if not root:
5 res.append('x')
6 return
7 res.append(root.val)
8 dfs(root.left)
9 dfs(root.right)
10 dfs(root)
11 return ' '.join(res)
12
1import java.util.StringJoiner;
2
3public static String serialize(Node root) {
4 StringJoiner res = new StringJoiner(" ");
5 serializeDFS(root, res);
6 return res.toString();
7}
8
9private static void serializeDFS(Node root, StringJoiner result) {
10 if (root == null) {
11 result.add("x");
12 return;
13 }
14 result.add(Integer.toString(root.val));
15 serializeDFS(root.left, result);
16 serializeDFS(root.right, result);
17}
18
1function serialize(root) {
2 let res = [];
3 serialize_dfs(root, res);
4 return res.join(" ");
5}
6
7function serialize_dfs(root, res) {
8 if (!root) {
9 res.push("x");
10 return;
11 }
12 res.push(root.val);
13 serialize_dfs(root.left, res);
14 serialize_dfs(root.right, res);
15}
16
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
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
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
Want a Structured Path to Master System Design Too? Don’t Miss This!