Problem overview
The function countPermutations receives a non-negative Int named n and returns the number of permutations of length n: the ways to arrange the numbers 1 to n in a row, using each exactly once. Only the count is needed. The statement keeps n at 12 or below.
For n = 3 the six 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
To build a permutation of length i, first choose which number goes in front. There are i choices. Whatever the choice, the remaining i - 1 numbers can be arranged in any of the ways that permutations of length i - 1 can, because only their relative order matters, not which numbers they are. So the count for length i is i times the count for length i - 1.
With dp[i] as the number of permutations of length i, that is dp[i] = i * dp[i - 1]. This is the factorial, i!. The base case is dp[0] = 1: there is exactly one way to arrange nothing, the empty permutation. Then dp[1] = 1 * dp[0] = 1, dp[2] = 2, dp[3] = 6, and so on.
Starting at length 0 instead of length 1 also answers n = 0 correctly, with no special case.
Step by step
- Create
dpas anIntArrayof sizen + 1, so indexnexists. It starts with zeros. - Set
dp[0] = 1, the empty permutation. Every longer length is built from it. Forn = 0the array has only index 0, which is exactly the one that was set. - Loop
iover1..nin increasing order, sodp[i - 1]is always ready when needed. - Set
dp[i] = i * dp[i - 1]:ichoices for the first element times the arrangements of the rest. - Return
dp[n]below the loop.
The solution in Kotlin
class Solution {
fun countPermutations(n: Int): Int {
val dp = IntArray(n + 1)
dp[0] = 1
for (i in 1..n) {
dp[i] = i * dp[i - 1]
}
return dp[n]
}
}
The Kotlin details worth a closer look:
- Inclusive range.
1..nincludesn. Whennis 0, the range is empty and the loop never runs. - Fixed-size array.
IntArray(n + 1)has exactlyn + 1slots. Writing to a slot beyond that throwsArrayIndexOutOfBoundsException. - Silent overflow.
Intholds values up to 2,147,483,647. 12! is 479,001,600, which fits, so the statement's limit of 12 is safe. 13! is 6,227,020,800, which doesn't:countPermutations(13)returns 1932053504, a wrong number with no error. UseLongif you need larger values, which works up ton = 20.
Complexity
Time: O(n). The loop runs n times with one multiplication each.
Space: O(n). The table has n + 1 entries. Since each entry depends only on the one before it, a single running product would use O(1) space. The table shows the recurrence and keeps all the smaller answers visible, which is the idea behind dynamic programming.
Edge cases and common mistakes
n = 0. The answer is 1, and the code returnsdp[0], which is 1.n = 1. The answer is 1.- Base case 0. If
dp[0]were left at 0, every entry after it would be 0 too, since each one multiplies the previous. - Multiplying by
i - 1. The first element hasichoices, noti - 1. That mistake gives 0 ati = 1. - Exceeding 12. Past 12 the
Intresult wraps around without any error. - Mixing up with combinations. This counts ordered arrangements of all
nnumbers. Choosing a subset is a different count.
Testing
n = 0gives 1.n = 1gives 1.n = 3gives 6.n = 12gives 479001600.