Facebook Pixel

3765. Complete Prime Number

MediumMathEnumerationNumber Theory
LeetCode ↗

Problem Description

You are given an integer num.

A number num is called a Complete Prime Number if every prefix and every suffix of num is prime.

Your task is to return true if num is a Complete Prime Number, otherwise return false.

Here are some important points to understand:

  • A prefix of a number is formed by taking the first k digits of the number. For example, the prefixes of 233 are 2, 23, and 233.
  • A suffix of a number is formed by taking the last k digits of the number. For example, the suffixes of 233 are 3, 33, and 233.
  • A single-digit number is considered a Complete Prime Number only if the digit itself is prime (that is, 2, 3, 5, or 7).

In short, you need to check all prefixes and all suffixes of num. The number qualifies as a Complete Prime Number only when every one of these values is prime.

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

How We Pick the Algorithm

Why Number Theory?

This problem maps to Number Theory through a short path in the full flowchart.

Prime orGCD/LCM?yesGraph ortree?noNumber Theory

Checking whether every prefix and suffix of a number is prime involves primality testing, a classic number theory operation.

Open in Flowchart

Intuition

The problem asks us to verify a condition over every prefix and every suffix of num. This naturally breaks the task into two independent checks: one for all prefixes and one for all suffixes. If both checks pass, the number is a Complete Prime Number.

The first thing we need is a way to test whether a single number is prime. A number x is prime if it has no divisors other than 1 and itself. Instead of checking every integer up to x, we only need to check divisors from 2 up to sqrt(x). This is because if x has a divisor larger than sqrt(x), it must also have a matching divisor smaller than sqrt(x), so we would have already found it. This gives us an efficient is_prime helper.

Next, we need to generate all the prefixes and suffixes. The cleanest way to handle the digits is to convert num into a string s. Once we have the string, we can build the numeric values step by step:

  • For prefixes, we read the digits from left to right. Starting from 0, each time we see a new digit c, we update our value as x = x * 10 + int(c). This grows the number one digit at a time, naturally producing 2, then 23, then 233, and so on.

  • For suffixes, we read the digits from right to left. We keep a place value p (which starts at 1 and multiplies by 10 each step) and update x = p * int(c) + x. This builds the suffix values from the last digit inward, producing 3, then 33, then 233.

The key insight is that by constructing these numbers incrementally, we avoid repeatedly slicing the string and re-parsing it. As soon as any prefix or suffix turns out to be not prime, we can immediately return false, since the Complete Prime condition requires all of them to be prime. If we finish both loops without finding a non-prime value, we return true.

Pattern Learn more about Math patterns.

Solution Approach

We follow a Mathematics-based approach, combining a primality test with incremental number construction.

Step 1: The primality test

We define a helper function is_prime(x) to determine whether a number x is prime:

  • If x < 2, then x is not prime, so we return False.
  • Otherwise, we check every integer i in the range from 2 to int(sqrt(x)) + 1. If any i divides x evenly (that is, x % i == 0), then x is not prime. In the code, all(x % i for i in range(2, int(sqrt(x)) + 1)) returns True only when none of the values in that range divide x, meaning x is prime.

Checking up to sqrt(x) is sufficient because any factor larger than sqrt(x) must pair with a factor smaller than sqrt(x).

Step 2: Convert the number to a string

We convert num into a string s using s = str(num). This lets us access each digit easily and process them in order.

Step 3: Check all prefixes (left to right)

We initialize x = 0. Then we iterate over each character c in s from left to right:

  • Update the prefix value with x = x * 10 + int(c). This appends the new digit to the end of the current number, growing the prefix one digit at a time.
  • Call is_prime(x). If it returns False, we immediately return False, because a non-prime prefix disqualifies num.

Step 4: Check all suffixes (right to left)

We reset x = 0 and introduce a place value p = 1. Then we iterate over each character c in the reversed string s[::-1]:

  • Update the suffix value with x = p * int(c) + x. Here p represents the positional weight (1, 10, 100, ...) of the current digit, so we place the new digit in front of the existing suffix.
  • Multiply p by 10 with p *= 10 to prepare for the next digit.
  • Call is_prime(x). If it returns False, we return False right away.

Step 5: Final result

If both loops complete without finding any non-prime prefix or suffix, every prefix and suffix is prime, so we return True.

Complexity Analysis

Let n be the number of digits in num, and let M be the value of num itself.

  • Time complexity: There are O(n) prefixes and O(n) suffixes. For each, the primality check costs O(sqrt(M)). Thus the overall time is O(n * sqrt(M)).
  • Space complexity: O(n) for storing the string representation s, while the incremental construction uses only a constant amount of extra space.

Example Walkthrough

Let's trace through the solution approach using a small example: num = 233.

Setup: Convert to string

We start by converting the number into a string so we can process each digit:

s = "233"

Step 1: Check all prefixes (left to right)

We initialize x = 0 and read the digits from left to right, building each prefix incrementally with x = x * 10 + int(c).

Digit cComputation x = x * 10 + int(c)Prefix value xis_prime(x)?
20 * 10 + 22✅ prime
32 * 10 + 323✅ prime
323 * 10 + 3233✅ prime

Let's verify one primality check in detail. For x = 23, we test divisors from 2 up to int(sqrt(23)) + 1 = 5:

  • 23 % 2 = 1 (not divisible)
  • 23 % 3 = 2 (not divisible)
  • 23 % 4 = 3 (not divisible)

No divisor is found, so 23 is prime. ✅

All prefixes (2, 23, 233) are prime, so we do not return false here and proceed to the suffixes.

Step 2: Check all suffixes (right to left)

We reset x = 0 and introduce the place value p = 1. We read the digits from right to left over s[::-1] = "332", building each suffix with x = p * int(c) + x, then updating p *= 10.

Digit cPlace pComputation x = p * int(c) + xSuffix value xis_prime(x)?New p
311 * 3 + 03✅ prime10
31010 * 3 + 333❌ not prime100

When we reach the suffix 33, the primality check fails. Testing divisors from 2 to int(sqrt(33)) + 1 = 6:

  • 33 % 3 = 0 (divisible!)

Since 33 = 3 * 11, it is not prime. The moment is_prime(33) returns False, we immediately return false without examining the remaining suffix 233.

Result

num = 233 is not a Complete Prime Number, so the function returns false. Even though every prefix was prime, the suffix 33 broke the condition, which demonstrates why both checks are required.


A passing example for contrast: num = 23

To see a true result, consider num = 23:

  • Prefixes: 2 (prime ✅), 23 (prime ✅)
  • Suffixes: 3 (prime ✅), 23 (prime ✅)

Every prefix and every suffix is prime, so both loops complete without returning early, and the function returns true. This shows the case where all incremental constructions pass the primality test and the final return True is reached.

Solution Implementation

1from math import isqrt
2
3
4class Solution:
5    def completePrime(self, num: int) -> bool:
6        # Helper that checks whether a single integer is prime.
7        def is_prime(value: int) -> bool:
8            if value < 2:
9                return False
10            # Test divisibility only up to the integer square root of value.
11            # all(...) returns False as soon as a divisor is found.
12            return all(value % divisor for divisor in range(2, isqrt(value) + 1))
13
14        digits = str(num)
15
16        # Check every prefix: build the number digit by digit from the left.
17        # e.g. for "239" -> 2, 23, 239
18        prefix = 0
19        for char in digits:
20            prefix = prefix * 10 + int(char)
21            if not is_prime(prefix):
22                return False
23
24        # Check every suffix: build the number digit by digit from the right.
25        # e.g. for "239" -> 9, 39, 239
26        suffix, place_value = 0, 1
27        for char in reversed(digits):
28            suffix = place_value * int(char) + suffix
29            place_value *= 10
30            if not is_prime(suffix):
31                return False
32
33        # All prefixes and suffixes are prime.
34        return True
35
1class Solution {
2    /**
3     * Determines whether the given number is a "complete prime".
4     * A number qualifies only if:
5     *   1. Every prefix (read left to right) is prime.
6     *   2. Every suffix (read left to right within the number) is prime.
7     *
8     * @param num the number to validate
9     * @return true if all prefixes and suffixes are prime, false otherwise
10     */
11    public boolean completePrime(int num) {
12        // Convert the number into its individual digit characters.
13        char[] digits = String.valueOf(num).toCharArray();
14
15        // ---- Check every prefix (built from left to right). ----
16        int prefixValue = 0;
17        for (int i = 0; i < digits.length; i++) {
18            // Append the current digit to the growing prefix.
19            prefixValue = prefixValue * 10 + (digits[i] - '0');
20            // If any prefix is not prime, the number fails immediately.
21            if (!isPrime(prefixValue)) {
22                return false;
23            }
24        }
25
26        // ---- Check every suffix (built from right to left). ----
27        int suffixValue = 0;
28        int placeValue = 1; // Tracks the positional weight (1, 10, 100, ...).
29        for (int i = digits.length - 1; i >= 0; i--) {
30            // Add the current digit at its correct positional weight.
31            suffixValue = placeValue * (digits[i] - '0') + suffixValue;
32            placeValue *= 10;
33            // If any suffix is not prime, the number fails immediately.
34            if (!isPrime(suffixValue)) {
35                return false;
36            }
37        }
38
39        // All prefixes and suffixes are prime.
40        return true;
41    }
42
43    /**
44     * Checks whether a given value is a prime number.
45     *
46     * @param value the value to test
47     * @return true if value is prime, false otherwise
48     */
49    private boolean isPrime(int value) {
50        // Numbers less than 2 are not prime by definition.
51        if (value < 2) {
52            return false;
53        }
54        // Test divisibility up to the square root of value.
55        for (int i = 2; i * i <= value; i++) {
56            if (value % i == 0) {
57                return false;
58            }
59        }
60        return true;
61    }
62}
63
1class Solution {
2public:
3    bool completePrime(int num) {
4        // Helper lambda to check whether a given value is a prime number.
5        auto isPrime = [&](int value) {
6            // Any number less than 2 is not prime.
7            if (value < 2) {
8                return false;
9            }
10            // Trial division up to sqrt(value) to detect factors.
11            for (int i = 2; i * i <= value; ++i) {
12                if (value % i == 0) {
13                    return false;
14                }
15            }
16            return true;
17        };
18
19        // Convert the number to its string representation for digit-wise processing.
20        string digits = to_string(num);
21
22        // Check every prefix (left-to-right) of the number for primality.
23        int prefix = 0;
24        for (char c : digits) {
25            // Append the current digit to build the next prefix value.
26            prefix = prefix * 10 + (c - '0');
27            if (!isPrime(prefix)) {
28                return false;
29            }
30        }
31
32        // Check every suffix (right-to-left) of the number for primality.
33        int suffix = 0;
34        int placeValue = 1;
35        for (int i = (int) digits.size() - 1; i >= 0; --i) {
36            // Prepend the current digit using the running place value.
37            suffix = placeValue * (digits[i] - '0') + suffix;
38            placeValue *= 10;
39            if (!isPrime(suffix)) {
40                return false;
41            }
42        }
43
44        // All prefixes and suffixes are prime, so the number is a "complete prime".
45        return true;
46    }
47};
48
1/**
2 * Determines whether a number is a "complete prime", meaning every prefix
3 * (read left-to-right) and every suffix (read right-to-left) of the number is prime.
4 *
5 * @param num - The number to evaluate.
6 * @returns True if all prefixes and suffixes are prime, otherwise false.
7 */
8function completePrime(num: number): boolean {
9    /**
10     * Checks whether a given value is a prime number.
11     *
12     * @param value - The candidate number to test for primality.
13     * @returns True if the value is prime, otherwise false.
14     */
15    const isPrime = (value: number): boolean => {
16        // Any value below 2 cannot be prime.
17        if (value < 2) {
18            return false;
19        }
20        // Trial division up to the square root of the value.
21        for (let divisor = 2; divisor * divisor <= value; divisor++) {
22            if (value % divisor === 0) {
23                return false;
24            }
25        }
26        return true;
27    };
28
29    // Convert the number to its string representation to iterate over digits.
30    const digits = String(num);
31
32    // ----- Check every prefix (left to right) -----
33    let prefixValue = 0;
34    for (let i = 0; i < digits.length; i++) {
35        // Append the current digit to the accumulated prefix value.
36        // (charCodeAt - 48) converts the character to its numeric value.
37        prefixValue = prefixValue * 10 + (digits.charCodeAt(i) - 48);
38        if (!isPrime(prefixValue)) {
39            return false;
40        }
41    }
42
43    // ----- Check every suffix (right to left) -----
44    let suffixValue = 0;
45    let placeValue = 1; // Tracks the current digit's positional weight (1, 10, 100, ...).
46    for (let i = digits.length - 1; i >= 0; i--) {
47        // Add the current digit, scaled by its place value, to the suffix.
48        suffixValue = placeValue * (digits.charCodeAt(i) - 48) + suffixValue;
49        placeValue *= 10;
50        if (!isPrime(suffixValue)) {
51            return false;
52        }
53    }
54
55    // All prefixes and suffixes are prime.
56    return true;
57}
58

Time and Space Complexity

  • Time complexity: O(sqrt(n) × log n). Let n be the value of the integer num, so the number of digits in num is O(log n). The code processes the digits in two passes (the prefixes from left to right and the suffixes from right to left), each pass iterating O(log n) times. In every iteration, the function is_prime(x) is called, and since x is bounded by n, each primality check costs O(sqrt(x)) = O(sqrt(n)). Therefore the overall time complexity is O(log n × sqrt(n)) = O(sqrt(n) × log n).

  • Space complexity: O(log n). The string s = str(num) stores all digits of num, which requires O(log n) space. The range in is_prime is a lazy iterator under all, consuming only O(1) additional space, and the remaining variables (x, p) also use O(1) space. Thus the dominant term is the string storage, giving O(log n).

Pattern Learn more about how to find time and space complexity quickly.

Common Pitfalls

Pitfall 1: Mishandling suffixes that contain leading zeros

The most common mistake is treating a suffix purely as a numeric value while ignoring the meaning of leading zeros. When a digit 0 appears in the middle of num, the suffix that starts at that 0 will have a leading zero.

For example, consider num = 307:

  • Suffixes (as strings) are "7", "07", and "307".
  • If you naively convert "07" to an integer, you get 7, which is prime, so the suffix check silently passes.

But conceptually "07" is not a valid 2-digit prime — it has a leading zero. Depending on how the problem defines a "suffix," collapsing "07" into 7 can produce a false positive. The incremental construction in the code does exactly this collapse:

suffix = place_value * int(char) + suffix   # "07" becomes 0*10 + 7 = 7

Solution: Decide explicitly whether a leading-zero suffix is allowed. If it is not, reject any suffix whose leading digit is 0 (except the single digit 0 itself, which is not prime anyway):

suffix, place_value = 0, 1
for char in reversed(digits):
    if char == '0' and place_value > 1:
        # A multi-digit suffix starting with '0' is invalid.
        return False
    suffix = place_value * int(char) + suffix
    place_value *= 10
    if not is_prime(suffix):
        return False

This guarantees that a value like 307 is correctly rejected, because the suffix "07" is recognized as malformed rather than being silently reduced to a prime.


Pitfall 2: Inefficient primality testing for large numbers

The primality check runs in O(sqrt(M)), where M is the value of the largest prefix/suffix (essentially num itself). For numbers with many digits, M grows exponentially with the digit count n, so sqrt(M) becomes very large and the trial-division test becomes a bottleneck.

Solution: Use a faster deterministic primality test, such as the Miller–Rabin test, which runs in roughly O(log³ M) time with a fixed set of witnesses that is provably correct for all 64-bit integers:

def is_prime(n: int) -> bool:
    if n < 2:
        return False
    for p in (2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37):
        if n % p == 0:
            return n == p
    d, r = n - 1, 0
    while d % 2 == 0:
        d //= 2
        r += 1
    for a in (2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37):
        x = pow(a, d, n)
        if x == 1 or x == n - 1:
            continue
        for _ in range(r - 1):
            x = x * x % n
            if x == n - 1:
                break
        else:
            return False
    return True

This keeps performance stable even when num has many digits.


Pitfall 3: Redundant work between prefix and suffix checks

The full number num is both the longest prefix and the longest suffix, so is_prime(num) is computed twice. For a single call this is harmless, but if completePrime is invoked many times (or num is large), the repeated work adds up.

Solution: Compute the primality of the full number once and reuse it, or simply note that if the longest prefix already failed, the suffix loop is never reached. A small early-exit optimization is to check the full number first:

if not is_prime(num):
    return False

This prunes most non-qualifying inputs immediately before doing any per-digit work.

Ready to land your dream job?

Unlock your dream job with a 5-minute quiz for a personalized study roadmap!

Get My Roadmap
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Get a Personalized Study Roadmap:

How does merge sort divide the problem into subproblems?


Recommended Readings

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

Load More