Problem overview
The function count_permutations takes n and returns how many permutations of length n exist: orderings of the numbers 1 through n, each used once. The statement limits N to 12.
For n = 3 the permutations are [1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2] and [3, 2, 1], so the answer is 6.
Approach: choose the first element, then arrange the rest
Let dp[i] be the number of permutations of length i. To build one, pick its first element: there are i choices. Whatever was picked, the remaining i - 1 elements can be arranged in dp[i - 1] ways, since only their number matters and not which values they are. So:
dp[i] = i * dp[i - 1]
This is the factorial, i!. The base case is dp[0] = 1: there is exactly one permutation of nothing, the empty one. Starting from length 0 instead of 1 has a second benefit, covered below: it makes n = 0 work with no special case.
Step by step
- Create
dpasvec![0i32; n + 1], declaredlet mut. The starting code has already shadowednwith ausize, so it can serve as both a size and an index. The extra slot makes indexnexist. - Set
dp[0] = 1. This is 0! = 1. Even whennis 0 and the vector has only index 0, the final lookup works. - Loop over
1..=n, so each length is computed after the one before it. - Multiply:
dp[i] = (i as i32) * dp[i - 1]. The indexiis ausize, and Rust doesn't multiply ani32by ausize, soiis converted first. - Return
dp[n]as the final expression.
The solution in Rust
impl Solution {
fn count_permutations(n: i32) -> i32 {
let n = n as usize;
let mut dp = vec![0i32; n + 1];
dp[0] = 1;
for i in 1..=n {
dp[i] = (i as i32) * dp[i - 1];
}
dp[n]
}
}
The Rust details worth a closer look:
- Shadowing.
let n = n as usize;declares a new variable that hides thei32parameter. The converted value is used from then on, which is cleaner than casting at every use. - The suffix in
0i32. It fixes the element type of the vector. The multiplication must be between twoi32values, and the suffix makes the type explicit. - Casting with
as.n as usizewould turn a negativeninto an enormous number, so the code relies on the statement's non-negative input.i as i32is safe becauseiis at most 12. - Overflow. 12! = 479,001,600 fits in an
i32, and 13! = 6,227,020,800 does not. That is why the statement stops at 12. In an optimized build, going past that would wrap silently, and in a debug build it would panic.
Complexity
Time: O(n). One multiplication per length.
Space: O(n). The table has n + 1 entries. Keeping only the running product would use O(1).
Edge cases and common mistakes
- N = 0. Returns 1. The vector has one entry, the loop
1..=0is empty, anddp[0]is 1. - N = 1. Returns 1, since 1 *
dp[0]is 1. - Starting the table at 1. With
dp[1] = 1as the base,n = 0would need a separate case or it would index past the end. - Multiplying by
i - 1. That gives 0 ati = 1and zeroes everything after it. The number of choices for the first element isi. - Forgetting the cast.
i * dp[i - 1]doesn't compile, becauseusizeandi32are different types.
Testing
n = 0gives 1.n = 1gives 1.n = 3gives 6.n = 12gives 479001600.
