Input: digits =[3,1,2]Output: 5Explanation: The sorted permutation list is[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]. The rank of [3,1,2]is5(1-based).
The brute force approach involves generating every possible permutation of the input digits, sorting all these permutations in lexicographic order, and then finding the position (rank) of the original number in this sorted list. While this method is easy to understand, it is highly inefficient because the number of permutations grows factorially with the number of digits, making it impractical for large inputs.
This method uses combinatorial mathematics to efficiently determine the rank of a given permutation without generating all possible permutations.
Take the number X = 415, represented as the digits [4, 5, 1]. Start by assuming the rank is 1.
For each digit, calculate how many permutations can be formed by placing a smaller digit in the current position and arranging the remaining digits. For example:
At position 0 (digit 4), there is one smaller digit (1). Placing 1 at position 0 allows for 2! arrangements of the remaining digits, so rank += 2 (rank = 3).
Move to position 1 (digit 5). There is one smaller digit (1) among the remaining digits. Placing 1 at position 1 allows for 1! arrangement, so rank += 1 (rank = 4).
At position 2 (digit 1), there are no smaller digits left, so no additional permutations are added.
Repeat this process for each position. The final rank is the sum of all such contributions, plus the initial rank of 1.
The provided code implements this logic. It runs in O(n^2) time because counting smaller digits to the right of each position is O(n^2). This can be improved to O(n log n) using inversion counting or advanced data structures. For more details, see: Count Inversions - Count Smaller on Right.
publicstaticint[]smallerCountOnRight(finalint[] digits) {
int[] smaller =newint[digits.length];
// For each digit, count how many digits to its right are smallerfor (int i = 0; i < digits.length; i++) {
for (int j = i + 1; j < digits.length; j++) {
if (digits[j]< digits[i]) {
smaller[i]++;
}
}
}
return smaller;
}
publicstaticintpermRank(finalint[] digits) {
int[] smallerCount = smallerCountOnRight(digits);
int[] factorial =newint[digits.length+ 1];
factorial[0]= 1;
// Compute factorials up to nfor (int i = 1; i <= digits.length; i++) {
factorial[i]= i * factorial[i - 1];
}
int rank = 1;
for (int i = 0; i < digits.length; i++) {
rank += smallerCount[i]* factorial[digits.length- i - 1];
}
return rank;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from typing import List
defsmaller_count_on_right(digits: List[int]) -> List[int]:
smaller = [0] * len(digits)
for i in range(len(digits)):
for j in range(i +1, len(digits)):
if digits[j] < digits[i]:
smaller[i] +=1return smaller
defperm_rank(digits: List[int]) -> int:
smaller_count = smaller_count_on_right(digits)
factorial = [1] * (len(digits) +1)
for i in range(1, len(digits) +1):
factorial[i] = i * factorial[i -1]
rank =1for i in range(len(digits)):
rank += smaller_count[i] * factorial[len(digits) - i -1]
return rank
⏰ Time complexity: O(n^2) — For each digit, we count smaller digits to its right, and compute factorials.
🧺 Space complexity: O(n) — Uses extra space for the count and factorial arrays.