Facebook Pixel

3556. Sum of Largest Prime Substrings

MediumHash TableMathStringNumber TheorySorting
Leetcode Link

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" becomes 12)
  • Each unique prime number is counted only once, even if it appears multiple times through different substrings

The task requires you to:

  1. Extract all possible substrings from the given string
  2. Convert each substring to an integer and check if it's a prime number
  3. Collect all unique prime numbers found
  4. 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).

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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:

  1. We must examine all substrings anyway - there's no way to know which substrings form primes without checking them
  2. A substring starting at position i and ending at position j 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.

Learn more about Math and Sorting patterns.

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 from 0 to n-1 (starting position)
    • Inner loop: j from i to n-1 (ending position)
  • For each starting position i, initialize x = 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

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 to sqrt(x) divides x
    • 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 set st

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 Evaluator

Example 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}
  • 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}
  • 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}
  • 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}

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 (where n 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 where x 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)
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

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

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More