题目形容

这是 LeetCode 上的 1175. 质数排列 ,难度为 简略

Tag : 「数学」、「组合数」、「二分」、「打表」

请你帮忙给从 $1$ 到 $n$ 的数设计排列计划,使得所有的「质数」都应该被放在「质数索引」(索引从 $1$ 开始)上;你须要返回可能的计划总数。

让咱们一起来回顾一下「质数」:质数肯定是大于 $1$ 的,并且不能用两个小于它的正整数的乘积来示意。

因为答案可能会很大,所以请你返回答案 模 mod $10^9 + 7$ 之后的后果即可。

示例 1:

输出:n = 5输入:12解释:举个例子,[1,2,5,4,3] 是一个无效的排列,但 [5,2,3,4,1] 不是,因为在第二种状况里质数 5 被谬误地放在索引为 1 的地位上。

示例 2:

输出:n = 100输入:682289015

提醒:

  • $1 <= n <= 100$

打表 + 二分 + 数学

依据题意,可将问题转换为求 $n$ 以内的质数个数,记为 $a$,同时可得非质数个数为 $b = n - a$。

质数的搁置计划数为 $a!$,而非质数的搁置计划数为 $b!$,依据「乘法原理」总的搁置计划数为 $a! \times b!$。

咱们能够通过「打表」的形式将 $100$ 以内的质数预处理到数组 list 中,对于每个 $n$ 而言,咱们找到第一个满足「值小于等于 $n$」的地位,从而得悉 $n$ 范畴以内的质数个数。

代码:

class 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{n})$,计算计划数的复杂度为 $O(n)$。整体复杂度为 $O(n)$
  • 空间复杂度:$O(C)$,其中 $C = 25$ 为 $100$ 以内的质数个数

打表 + 数学

更进一步,对于特定的 $n$ 而言,咱们在预处理 $100$ 以内的质数时,曾经能够确定在 $[1, n]$ 内有多少个质数,从而省掉二分操作。

应用数组 cnts 记录下不超过以后值范畴内质数的个数,$cnts[i] = x$ 含意为在 $[1, i]$ 范畴内质数数量为 $x$。

代码:

class 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/SharingSou... 。

在仓库地址里,你能够看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其余优选题解。

更多更全更热门的「口试/面试」相干材料可拜访排版精美的 合集新基地

本文由mdnice多平台公布