3848. Check Digitorial Permutation
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 asx!, is the product of all positive integers less than or equal tox, and0! = 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.
How We Pick the Algorithm
Why Math / Bit Manipulation?
This problem maps to Math / Bit Manipulation through a short path in the full flowchart.
Computes digit factorial sum and compares digit multisets using pure math operations.
Open in FlowchartIntuition
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)tox. - 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) = 1f(4) = 4 × 3 × 2 × 1 = 24f(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:
| Iteration | y before | y % 10 (digit) | f(digit) | x after | y after (y // 10) |
|---|---|---|---|---|---|
| 1 | 145 | 5 | 120 | 0 + 120 = 120 | 14 |
| 2 | 14 | 4 | 24 | 120 + 24 = 144 | 1 |
| 3 | 1 | 1 | 1 | 144 + 1 = 145 | 0 |
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, sox = 9. - Compare:
sorted("9") = ['9']versussorted("123") = ['1', '2', '3']. - The lists differ (and even have different lengths), so no permutation of
123is digitorial. We returnfalse.
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))
251class 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}
421class 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};
371// 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}
36Time 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 RoadmapWhich data structure is used to implement priority queue?
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 If you prefer videos here's a video that explains recursion in a fun and easy way 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 him
Want a Structured Path to Master System Design Too? Don’t Miss This!