Facebook Pixel

3848. Check Digitorial Permutation

MediumMathCounting
LeetCode ↗

Problem Description

You are given an integer n.

A number is called digitorial if the sum of the factorials of its digits is equal to the number itself.

Determine whether any permutation of n (including the original order) forms a digitorial number.

Return true if such a permutation exists, otherwise return false.

Note:

  • The factorial of a non-negative integer x, denoted as x!, is the product of all positive integers less than or equal to x, and 0! = 1.
  • A permutation is a rearrangement of all the digits of a number that does not start with zero. Any arrangement starting with zero is invalid.

In short, you need to figure out if the digits of n can be rearranged into some number m (without a leading zero) such that m equals the sum of the factorials of its own digits. Since rearranging the digits does not change the multiset of digits, the sum of the factorials of the digits stays the same for every permutation. This means you only have to compute the factorial sum of the digits of n once, and then check whether that sum uses exactly the same digits as n.

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

How We Pick the Algorithm

Why Math / Bit Manipulation?

This problem maps to Math / Bit Manipulation through a short path in the full flowchart.

Math orbitmanipulation?yesSimulationorstraightforward?noMath / BitManipulation

Computes digit factorial sum and compares digit multisets using pure math operations.

Open in Flowchart

Intuition

The key observation is that a permutation only rearranges the digits of n; it never changes which digits are present or how many times each appears. Because the digitorial condition depends on the sum of the factorials of the digits, and addition does not care about the order of its terms, every permutation of n produces the exact same factorial sum.

For example, if n = 145, then any rearrangement like 415 or 514 still has the digits {1, 4, 5}, so the factorial sum is always 1! + 4! + 5! = 1 + 24 + 120 = 145.

This insight lets us avoid generating all permutations. Instead, we compute one fixed value: the sum of the factorials of the digits of n. Call it x. If some permutation of n is digitorial, that permutation must equal x. But a permutation of n is just a rearrangement of n's digits, so x can only be such a permutation if x and n share the same multiset of digits.

Therefore the problem reduces to a simple check: compute x, then verify whether x and n are made up of the same digits. A convenient way to compare the digit multisets is to turn both numbers into strings, sort their characters, and check if the sorted results are equal. If they match, a valid digitorial permutation exists and we return true; otherwise we return false.

Pattern Learn more about Math patterns.

Solution Approach

Solution 1: Simulation

According to the problem description, no matter how the digits of number n are rearranged, the sum of factorials of its digits remains unchanged. Therefore, we only need to calculate the sum of factorials of each digit of n once, and then check whether the digit composition of this sum equals the digit composition of n.

Step 1: Compute the factorial of a single digit.

We define a helper function f(x) that returns x!. It is implemented recursively: when x < 2, the factorial is 1 (covering both 0! = 1 and 1! = 1); otherwise it returns x * f(x - 1). To avoid recomputing the same factorials repeatedly, the function is decorated with @cache, which memoizes results so each digit's factorial is calculated at most once.

Step 2: Accumulate the factorial sum of the digits of n.

We use two variables, x and y, where x starts at 0 (the running sum) and y starts at n. While y is non-zero, we repeatedly:

  • Extract the last digit with y % 10.
  • Add its factorial f(y % 10) to x.
  • Remove that digit with y //= 10.

When the loop ends, x holds the sum of the factorials of all digits of n.

Step 3: Compare digit compositions.

A valid permutation of n that is digitorial must equal x, and it must also be a rearrangement of n's digits. So x and n must contain exactly the same digits. We check this by converting both to strings, sorting their characters, and comparing:

sorted(str(x)) == sorted(str(n))

If the sorted character lists match, the two numbers share the same digit multiset, meaning some permutation of n equals x and is therefore digitorial, so we return true. Otherwise, we return false.

Complexity Analysis:

Let d be the number of digits of n. Computing the factorial sum takes O(d) time. Sorting the digit strings of x and n takes O(d log d) time. The factorial values are bounded since each digit is at most 9 (and 9! = 362880 is a constant), so the overall time complexity is O(d log d), and the space complexity is O(d) for storing the string representations.

Example Walkthrough

Let's trace through the solution using n = 145.

Step 1: Set up the factorial helper.

We have f(x) returning x!, with f(0) = 1 and f(1) = 1 as base cases. We'll need a few values:

  • f(1) = 1
  • f(4) = 4 × 3 × 2 × 1 = 24
  • f(5) = 5 × 4 × 3 × 2 × 1 = 120

Step 2: Accumulate the factorial sum of the digits of n.

We initialize x = 0 (running sum) and y = 145 (the number we peel digits from). We loop while y is non-zero:

Iterationy beforey % 10 (digit)f(digit)x aftery after (y // 10)
114551200 + 120 = 12014
214424120 + 24 = 1441
3111144 + 1 = 1450

The loop ends when y = 0. Now x = 145.

Step 3: Compare digit compositions.

We compare the sorted digit strings of x and n:

  • sorted(str(x)) = sorted("145") = ['1', '4', '5']
  • sorted(str(n)) = sorted("145") = ['1', '4', '5']

The two sorted lists are equal, so x and n share the same digit multiset. This means some permutation of n (here, n itself) equals x and is therefore digitorial. We return true.


A quick contrasting case: n = 123.

  • Factorial sum: 1! + 2! + 3! = 1 + 2 + 6 = 9, so x = 9.
  • Compare: sorted("9") = ['9'] versus sorted("123") = ['1', '2', '3'].
  • The lists differ (and even have different lengths), so no permutation of 123 is digitorial. We return false.

This shows why we never have to generate any permutations: every rearrangement of n's digits yields the same factorial sum x, so a single computation plus a digit-multiset comparison fully answers the question.

Solution Implementation

1from functools import cache
2
3
4@cache
5def factorial(x: int) -> int:
6    """Return the factorial of x (with 0! = 1! = 1)."""
7    if x < 2:
8        return 1
9    return x * factorial(x - 1)
10
11
12class Solution:
13    def isDigitorialPermutation(self, n: int) -> bool:
14        # digit_factorial_sum accumulates the sum of the factorials of n's digits.
15        digit_factorial_sum, remaining = 0, n
16
17        # Extract each digit from the least significant end and add its factorial.
18        while remaining:
19            digit_factorial_sum += factorial(remaining % 10)
20            remaining //= 10
21
22        # The number is a "digitorial permutation" if the digits of the
23        # factorial sum are a rearrangement of the digits of n.
24        return sorted(str(digit_factorial_sum)) == sorted(str(n))
25
1class Solution {
2    // Precomputed factorials for digits 0 through 9
3    private static final int[] FACTORIAL = new int[10];
4
5    // Static initializer block to populate the factorial table
6    static {
7        FACTORIAL[0] = 1; // 0! = 1
8        for (int i = 1; i < 10; i++) {
9            FACTORIAL[i] = FACTORIAL[i - 1] * i; // i! = (i-1)! * i
10        }
11    }
12
13    /**
14     * Determines whether the sum of the factorials of n's digits,
15     * when written out, is a permutation of the digits of n itself.
16     *
17     * @param n the input number to check
18     * @return true if the digit-factorial sum is a permutation of n, false otherwise
19     */
20    public boolean isDigitorialPermutation(int n) {
21        int factorialSum = 0; // Accumulates the sum of factorials of each digit
22        int remaining = n;    // Working copy of n used for digit extraction
23
24        // Extract each digit and add its factorial to the running sum
25        while (remaining > 0) {
26            factorialSum += FACTORIAL[remaining % 10]; // Add factorial of the last digit
27            remaining /= 10;                           // Drop the last digit
28        }
29
30        // Convert both the factorial sum and the original number to character arrays
31        char[] sumDigits = String.valueOf(factorialSum).toCharArray();
32        char[] numberDigits = String.valueOf(n).toCharArray();
33
34        // Sort both arrays so that a permutation comparison becomes a direct equality check
35        Arrays.sort(sumDigits);
36        Arrays.sort(numberDigits);
37
38        // The two are permutations of each other if their sorted digit arrays match
39        return Arrays.equals(sumDigits, numberDigits);
40    }
41}
42
1class Solution {
2public:
3    bool isDigitorialPermutation(int n) {
4        // Precompute factorials for digits 0-9 (lazy initialization)
5        static int factorial[10];
6        static bool isInitialized = false;
7
8        if (!isInitialized) {
9            factorial[0] = 1;
10            for (int i = 1; i < 10; ++i) {
11                factorial[i] = factorial[i - 1] * i;
12            }
13            isInitialized = true;
14        }
15
16        // Compute the sum of factorials of each digit of n
17        int digitFactorialSum = 0;
18        int remaining = n;
19
20        while (remaining > 0) {
21            digitFactorialSum += factorial[remaining % 10]; // add factorial of the last digit
22            remaining /= 10;                                 // drop the last digit
23        }
24
25        // Convert both numbers to strings for digit comparison
26        string sumDigits = to_string(digitFactorialSum);
27        string originalDigits = to_string(n);
28
29        // Sort the digits so a permutation check becomes an equality check
30        sort(sumDigits.begin(), sumDigits.end());
31        sort(originalDigits.begin(), originalDigits.end());
32
33        // n is a "digitorial permutation" if both share the exact same multiset of digits
34        return sumDigits == originalDigits;
35    }
36};
37
1// Precompute factorials for digits 0 through 9.
2const factorials: number[] = new Array(10);
3factorials[0] = 1;
4for (let i = 1; i < 10; i++) {
5    factorials[i] = factorials[i - 1] * i;
6}
7
8/**
9 * Determines whether the sum of the factorials of the digits of `n`
10 * is a permutation of the digits of `n` itself.
11 *
12 * @param n - The non-negative integer to check.
13 * @returns `true` if the digit-factorial sum is a permutation of `n`'s digits.
14 */
15function isDigitorialPermutation(n: number): boolean {
16    // Accumulate the sum of factorials of each digit.
17    let digitFactorialSum = 0;
18    // Working copy of `n` used to extract digits.
19    let remaining = n;
20
21    // Process each digit from least significant to most significant.
22    while (remaining > 0) {
23        const digit = remaining % 10;
24        digitFactorialSum += factorials[digit];
25        remaining = Math.floor(remaining / 10);
26    }
27
28    // Sort the digits of the factorial sum to form a canonical signature.
29    const sumSignature = digitFactorialSum.toString().split('').sort().join('');
30    // Sort the digits of the original number to form its canonical signature.
31    const originalSignature = n.toString().split('').sort().join('');
32
33    // The number qualifies when both signatures match (i.e. same multiset of digits).
34    return sumSignature === originalSignature;
35}
36

Time and Space Complexity

The time complexity is O(log n), and the space complexity is O(d). Here, n is the integer given in the problem.

The while loop iterates over each digit of n, processing y //= 10 until y becomes zero. Since the number of digits in n is proportional to log n, the loop runs O(log n) times. Each iteration involves a cached factorial lookup f(y % 10), which operates only on a single digit (values 0 to 9) and is therefore O(1) after caching. Sorting the string representations of x and n also takes O(log n · log(log n)), but the dominant simplification yields O(log n).

For space, the factorial function f is decorated with @cache, storing results for the limited set of digit inputs. This produces a preprocessing array of length d = 10, giving a space complexity of O(d).

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

Common Pitfalls

Pitfall 1: Forgetting the leading-zero constraint when the factorial sum has fewer digits

The most subtle trap is assuming that "matching digit multisets" automatically guarantees a valid permutation. The problem explicitly states that a permutation must not start with zero. The provided solution compares sorted(str(x)) == sorted(str(n)), which only checks that the two numbers share the same multiset of digits — it does not independently re-validate the leading-zero rule.

In practice this works out fine because of a hidden invariant: if x (the factorial sum) is itself a number that equals one of n's permutations, then x as written cannot have a leading zero (integers never display a leading zero). And since x is the target digitorial value, the permutation we care about is exactly x itself. So the comparison implicitly enforces validity.

The pitfall is reasoning incorrectly about the reverse direction. Consider a case where n = 100 and suppose the factorial sum happened to be 10. Then:

  • sorted(str(10))['0', '1']
  • sorted(str(100))['0', '0', '1']

These differ in length, so the comparison correctly returns False. The string comparison naturally handles digit-count mismatches because str(x) never includes leading zeros, so its length already reflects the "real" digit count. The danger is if a developer tries to "fix" this by zero-padding str(x) to match the length of str(n) — that would introduce phantom leading zeros and produce false positives.

Solution: Do not pad the factorial sum. Compare the raw string representations directly so that any difference in genuine digit count (caused by n having extra zeros) naturally produces a mismatch:

# Correct — relies on str(x) having no leading zeros:
return sorted(str(digit_factorial_sum)) == sorted(str(n))

# WRONG — padding reintroduces leading zeros and breaks the leading-zero rule:
# padded = str(digit_factorial_sum).zfill(len(str(n)))
# return sorted(padded) == sorted(str(n))

Pitfall 2: Mishandling the digit 0 in the factorial computation

A frequent mistake is implementing the factorial helper with a base case of x <= 1: return x or starting the recursion product at 0, which makes 0! evaluate to 0 instead of 1. Since digits commonly include 0, this single error silently corrupts the entire factorial sum.

For example, if 0! = 0 were used, the digit 0 would contribute nothing, and the sum of factorials for a number like 145 (a real digitorial: 1! + 4! + 5! = 1 + 24 + 120 = 145) would still work, but any number containing a 0 would be miscomputed.

Solution: Ensure the base case covers both 0 and 1, since 0! = 1! = 1:

@cache
def factorial(x: int) -> int:
    if x < 2:   # covers both 0! = 1 and 1! = 1
        return 1
    return x * factorial(x - 1)

Pitfall 3: Treating n as a string input or assuming a fixed digit count

Some implementations attempt to iterate over n directly with for d in n, which fails because n is an integer, not a string. Others hard-code assumptions about the number of digits.

Solution: Extract digits arithmetically with % 10 and // 10 (as the solution does), which works for any integer size and avoids type errors:

remaining = n
while remaining:
    digit_factorial_sum += factorial(remaining % 10)
    remaining //= 10

Note also the edge case n = 0: the while remaining loop never executes, leaving digit_factorial_sum = 0, and sorted(str(0)) == sorted(str(0)) correctly returns True.

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:

Which data structure is used to implement priority queue?


Recommended Readings

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

Load More