跳转至

634. 寻找数组的错位排列 🔒

题目描述

在组合数学中,如果一个排列中所有元素都不在原先的位置上,那么这个排列就被称为 错位排列

给定一个从 1n 升序排列的数组,返回 不同的错位排列 的数量 。由于答案可能非常大,你只需要将答案对 109+7 取余 输出即可。

 

示例 1:

输入: n = 3
输出: 2
解释: 原始的数组为 [1,2,3]。两个错位排列的数组为 [2,3,1] 和 [3,1,2]。

示例 2:

输入: n = 2
输出: 1

 

提示:

  • 1 <= n <= 106

解法

方法一:动态规划

我们定义 $f[i]$ 表示长度为 $i$ 的数组的错位排列的数量。初始时 $f[0] = 1$, $f[1] = 0$。答案即为 $f[n]$。

对于长度为 $i$ 的数组,我们考虑将数字 $1$ 放在哪个位置,假设放在第 $j$ 个位置,这里有 $i-1$ 种选择,那么接下来数字 $j$ 可以有两种选择:

  • 放在第 $1$ 个位置,那么剩下的 $i - 2$ 个位置可以有 $f[i - 2]$ 种错位排列,因此总共有 $(i - 1) \times f[i - 2]$ 种错位排列;
  • 不放在第 $1$ 个位置,那么相当于转化为了长度为 $i - 1$ 的数组的错位排列,因此总共有 $(i - 1) \times f[i - 1]$ 种错位排列。

综上,我们有如下状态转移方程:

$$ f[i] = (i - 1) \times (f[i - 1] + f[i - 2]) $$

最终答案即为 $f[n]$。注意答案的取模操作。

时间复杂度 $O(n)$,其中 $n$ 为数组的长度。空间复杂度 $O(1)$。

1
2
3
4
5
6
7
class Solution:
    def findDerangement(self, n: int) -> int:
        mod = 10**9 + 7
        f = [1] + [0] * n
        for i in range(2, n + 1):
            f[i] = (i - 1) * (f[i - 1] + f[i - 2]) % mod
        return f[n]
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class Solution {
    public int findDerangement(int n) {
        long[] f = new long[n + 1];
        f[0] = 1;
        final int mod = (int) 1e9 + 7;
        for (int i = 2; i <= n; ++i) {
            f[i] = (i - 1) * (f[i - 1] + f[i - 2]) % mod;
        }
        return (int) f[n];
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class Solution {
public:
    int findDerangement(int n) {
        long long f[n + 1];
        memset(f, 0, sizeof(f));
        f[0] = 1;
        const int mod = 1e9 + 7;
        for (int i = 2; i <= n; i++) {
            f[i] = (i - 1LL) * (f[i - 1] + f[i - 2]) % mod;
        }
        return f[n];
    }
};
1
2
3
4
5
6
7
8
9
func findDerangement(n int) int {
    f := make([]int, n+1)
    f[0] = 1
    const mod = 1e9 + 7
    for i := 2; i <= n; i++ {
        f[i] = (i - 1) * (f[i-1] + f[i-2]) % mod
    }
    return f[n]
}

方法二:动态规划(空间优化)

我们发现,状态转移方程中只与 $f[i - 1]$ 和 $f[i - 2]$ 有关,因此我们可以使用两个变量 $a$ 和 $b$ 来分别表示 $f[i - 1]$ 和 $f[i - 2]$,从而将空间复杂度降低到 $O(1)$。

1
2
3
4
5
6
7
class Solution:
    def findDerangement(self, n: int) -> int:
        mod = 10**9 + 7
        a, b = 1, 0
        for i in range(2, n + 1):
            a, b = b, ((i - 1) * (a + b)) % mod
        return b
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class Solution {
    public int findDerangement(int n) {
        final int mod = (int) 1e9 + 7;
        long a = 1, b = 0;
        for (int i = 2; i <= n; ++i) {
            long c = (i - 1) * (a + b) % mod;
            a = b;
            b = c;
        }
        return (int) b;
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class Solution {
public:
    int findDerangement(int n) {
        long long a = 1, b = 0;
        const int mod = 1e9 + 7;
        for (int i = 2; i <= n; ++i) {
            long long c = (i - 1) * (a + b) % mod;
            a = b;
            b = c;
        }
        return b;
    }
};
1
2
3
4
5
6
7
8
func findDerangement(n int) int {
    a, b := 1, 0
    const mod = 1e9 + 7
    for i := 2; i <= n; i++ {
        a, b = b, (i-1)*(a+b)%mod
    }
    return b
}

评论