LC 1175. 质数排列
题目描述
这是 LeetCode 上的 1175. 质数排列 ,难度为 简单。
请你帮忙给从 $1$ 到 $n$ 的数设计排列方案,使得所有的「质数」都应该被放在「质数索引」(索引从 $1$ 开始)上;你需要返回可能的方案总数。
让我们一起来回顾一下「质数」:质数一定是大于 $1$ 的,并且不能用两个小于它的正整数的乘积来表示。
由于答案可能会很大,所以请你返回答案 模 mod
$10^9 + 7$ 之后的结果即可。
示例 1:1
2
3
4输入:n = 5
输出:12
解释:举个例子,[1,2,5,4,3] 是一个有效的排列,但 [5,2,3,4,1] 不是,因为在第二种情况里质数 5 被错误地放在索引为 1 的位置上。
示例 2:1
2
3输入:n = 100
输出:682289015
提示:
- $1 <= n <= 100$
打表 + 二分 + 数学
根据题意,可将问题转换为求 $n$ 以内的质数个数,记为 $a$,同时可得非质数个数为 $b = n - a$。
质数的放置方案数为 $a!$,而非质数的放置方案数为 $b!$,根据「乘法原理」总的放置方案数为 $a! \times b!$。
我们可以通过「打表」的方式将 $100$ 以内的质数预处理到数组 list
中,对于每个 $n$ 而言,我们找到第一个满足「值小于等于 $n$」的位置,从而得知 $n$ 范围以内的质数个数。
代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26class Solution {
static int MOD = (int)1e9+7;
static List<Integer> list = new ArrayList<>();
static {
for (int i = 2; i <= 100; i++) {
boolean ok = true;
for (int j = 2; j * j <= i; j++) {
if (i % j == 0) ok = false;
}
if (ok) list.add(i);
}
}
public int numPrimeArrangements(int n) {
int l = 0, r = list.size() - 1;
while (l < r) {
int mid = l + r + 1 >> 1;
if (list.get(mid) <= n) l = mid;
else r = mid - 1;
}
int a = r + 1, b = n - a;
long ans = 1;
for (int i = b; i > 1; i--) ans = ans * i % MOD ;
for (int i = a; i > 1; i--) ans = ans * i % MOD ;
return (int)ans;
}
}
- 时间复杂度:二分的复杂度为 $O(\log{C})$,其中 $C = 25$ 为 $100$ 以内的质数个数;计算方案数的复杂度为 $O(n)$。整体复杂度为 $O(n)$
- 空间复杂度:$O(C)$,其中 $C = 25$ 为 $100$ 以内的质数个数
打表 + 数学
更进一步,对于特定的 $n$ 而言,我们在预处理 $100$ 以内的质数时,已经可以确定在 $[1, n]$ 内有多少个质数,从而省掉二分操作。
使用数组 cnts
记录下不超过当前值范围内质数的个数,$cnts[i] = x$ 含义为在 $[1, i]$ 范围内质数数量为 $x$。
代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22class Solution {
static int MOD = (int)1e9+7;
static int[] cnts = new int[110];
static {
List<Integer> list = new ArrayList<>();
for (int i = 2; i <= 100; i++) {
boolean ok = true;
for (int j = 2; j * j <= i; j++) {
if (i % j == 0) ok = false;
}
if (ok) list.add(i);
cnts[i] = list.size();
}
}
public int numPrimeArrangements(int n) {
int a = cnts[n], b = n - a;
long ans = 1;
for (int i = b; i > 1; i--) ans = ans * i % MOD ;
for (int i = a; i > 1; i--) ans = ans * i % MOD ;
return (int)ans;
}
}
- 时间复杂度:$O(n)$
- 空间复杂度:$O(C)$,其中 $C = 100$
最后
这是我们「刷穿 LeetCode」系列文章的第 No.1175
篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。
在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。
为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode 。
在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!