【C/C++】1175. 质数排列

294 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情


题目链接:1175. 质数排列

题目描述

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

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

由于答案可能会很大,所以请你返回答案 mod 109+710^9 + 7 之后的结果即可。

提示:

  • 1n1001 \leqslant n \leqslant 100

示例 1:

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

示例 2:

输入: n = 100
输出: 682289015

整理题意

题目给定一个正整数 n,让我们对 [1, n] 内的「质数」和「非质数」进行排列,排列的要求是:

  • 质数只能在质数位置上
  • 非质数只能在非质数位置上 让我们返回所有可能的排列方案数。

另外题目提示由于答案可能会很大,所以需要返回 mod 109+710^9 + 7 之后的答案结果即可。

解题思路分析

由于质数的位置和非质数的位置是相互独立的,所以我们可以使用 乘法原理 将「所有质数都放在质数索引上的方案数」×\times「所有非质数都放在非质数索引上的方案数」得到总的方案数。

那么如何求「所有质数都放在质数索引上的方案数」和「所有非质数都放在非质数索引上的方案数」呢,根据 组合数学 得知即求质数个数和非质数个数的阶乘即可。

进而问题转换为求 [1, n] 中质数的个数,也就是筛质数问题,筛质数的方法也有很多:

  1. 枚举筛质数
  2. 埃氏筛质数
  3. 线性筛质数 三种筛质数的时间复杂度排序为:线性筛质数 < 埃氏筛质数 < 枚举筛质数。

因为线性筛质数不属于笔试/面试范畴,所以不需要掌握。

那么我们就选择使用次优的 埃氏筛质数

20191027172732460.gif

在之前介绍过 埃氏筛质数,可以直接查看,这里重点讲解该题的解法。

通过埃氏筛质数得到 [1, n] 的质数个数 num,那么非质数的个数就是 n - num

具体实现

  1. 使用埃氏筛质数统计 [1, n] 的质数个数 num,从而得到非质数个数 n - num
  2. 使用组合数学计算「所有质数都放在质数索引上的方案数」和「所有非质数都放在非质数索引上的方案数」,即求质数个数和非质数个数的阶乘即可。
  3. 使用 乘法原理 计算总的方案数。「所有质数都放在质数索引上的方案数」×\times「所有非质数都放在非质数索引上的方案数」

复杂度分析

  • 时间复杂度:O(nlog2(log2n))O(n*log_2(log_2n)),求 n 个数中质数个数的时间复杂度为 O(nlog2(log2n))O(n*log_2(log_2n)),也就是埃氏筛质数的时间复杂度。阶乘的时间复杂度为 O(n)O(n),总的时间复杂度为 O(nlog2(log2n))O(n*log_2(log_2n))
  • 空间复杂度:O(n)O(n),我们需要 O(n)O(n) 的空间记录每个数是否为质数。

代码实现

class Solution {
public:
    int numPrimeArrangements(int n) {
        int mod = 1e9 + 7;
        bool prime[n + 1];
        //初始化为全部都是质数
        for(int i = 0; i <= n; i++) prime[i] = 1;
        // 0 和 1 不是质数
        prime[0] = prime[1] = 0;
        //线性筛质数
        for(int i = 2; i * i <= n; i++){
            if(prime[i]) for(int j = i * i; j <= n; j += i) prime[j] = 0;  
        }
        //统计质数的个数
        long long int num = 0;
        for(int i = 2; i <= n; i++) if(prime[i]) num++;
        //计算非质数的摆放方案数
        long long int a = 1;
        for(int i = 1; i <= n - num; i++) a = (a * i) % mod;
        //计算质数的摆放方案数
        long long int b = 1;
        for(int i = 1; i <= num; i++) b = (b * i) % mod;
        //乘法原理
        return (a * b) % mod;
    }
};

总结

  • 该题本质是求范围内的质数个数,这里需要掌握埃氏筛质数以及埃氏筛质数的时间复杂度结论,无需掌握其时间复杂度的证明。
  • 乘法原理组合数学 也是该题的核心。
  • 测试结果:

微信截图_20220630102739.png

结束语

生活就像一只储钱罐,你投入的每一分努力都会累积下来,在未来的某一天,令你惊喜无比。不必去羡慕别人拥有的东西,沉下心来,每天付出一点努力,时间终会给你想要的答案。新的一天,加油!