leetcode
leetcode 1751 ~ 1800
好子集的数目

好子集的数目

难度:

标签:

题目描述

代码结果

运行时间: 120 ms, 内存: 21.2 MB


/*
 * This solution uses Java Streams to process the input array and compute the number of good subsets.
 * We use a HashMap to store the frequency of each number and filter those that can form a valid product of distinct primes.
 * The implementation also handles numbers with repeated prime factors that cannot form good subsets.
 */
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class GoodSubsetsStream {
    private static final int MOD = 1000000007;
    private static final int[] PRIMES = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29};

    public int numberOfGoodSubsets(int[] nums) {
        Map<Integer, Long> freq = Arrays.stream(nums)
                .boxed()
                .collect(Collectors.groupingBy(n -> n, Collectors.counting()));

        long[] dp = new long[1 << PRIMES.length];
        dp[0] = 1;  // empty set

        freq.entrySet().stream()
            .filter(entry -> entry.getKey() > 1)
            .forEach(entry -> {
                int num = entry.getKey();
                long count = entry.getValue();
                int subsetMask = getPrimeMask(num);
                if (subsetMask != -1) {
                    IntStream.iterate((1 << PRIMES.length) - 1, i -> i >= 0, i -> i - 1)
                        .filter(state -> (state & subsetMask) == 0)
                        .forEach(state -> dp[state | subsetMask] = (dp[state | subsetMask] + dp[state] * count) % MOD);
                }
            });

        long result = IntStream.range(1, dp.length).mapToLong(i -> dp[i]).sum() % MOD;
        long countOfOnes = freq.getOrDefault(1, 0L);
        if (countOfOnes > 0) {
            result = (result * powMod(2, countOfOnes)) % MOD;
        }

        return (int) result;
    }

    private int getPrimeMask(int num) {
        int mask = 0;
        for (int i = 0; i < PRIMES.length; i++) {
            int prime = PRIMES[i];
            if (num % (prime * prime) == 0) return -1;
            if (num % prime == 0) mask |= (1 << i);
        }
        return mask;
    }

    private long powMod(long base, long exp) {
        long result = 1;
        while (exp > 0) {
            if (exp % 2 == 1) result = (result * base) % MOD;
            base = (base * base) % MOD;
            exp /= 2;
        }
        return result;
    }
}

解释

方法:

此题解通过考虑不同类型的子集并分情况处理来计算好子集的数目。主要分为三类情况:1.只包含质数的子集;2.包含一个合数的子集;3.包含两个合数的子集。对于每种情况,分别计算可能的子集数量并求和。此外,1的存在对子集的组合方式有特殊影响,因为任何数量的1都可以添加到子集中而不改变乘积的质数组成,因此最后的结果需要乘上2的1的个数次幂。

时间复杂度:

O(1)

空间复杂度:

O(1)

代码细节讲解

🦆
在解决这个问题时,你是如何确定哪些数字是质数,哪些是合数?
在解决问题的过程中,我预先定义了一个质数列表`pri`包含了所有小于30的质数。这样做是因为在给定的题目中,我们只关注数字30及以下的数字。通过这个列表,我们可以明确知道哪些数字是质数。对于合数的识别,由于题目只涉及到特定的合数(比如6、10、15等),所以我们通过组合列表中的质数来判断特定数字是否为合数。
🦆
为什么需要特别处理数字1,它在计算好子集中起到了什么作用?
数字1在计算好子集中需要特别处理,因为它是一个特殊的数字。1乘以任何数字的结果仍然是原数字,因此在形成子集时,1的存在不会改变子集乘积的质因数结构。这意味着任何一个有效的子集,我们都可以任意添加任意数量的1而不改变它作为好子集的资格。因此,在计算最终的子集数时,我们需要将所有不包含1的子集数乘以2的1的个数次幂,来包含所有可能加入1的情况。
🦆
解题中提到的情况2和情况3涉及包含一个合数和两个合数的子集,这些子集是如何被计算的?能否详细解释一下函数`ct`和`cth`的具体作用?
在题解中,`ct`函数用于计算包含恰好一个特定合数的子集数。它首先计算除了该合数涉及的质因数外的所有质数的组合数量,然后乘以该合数的出现次数。`cth`函数则用于处理情况3,即子集中包含两个不同的合数。这个函数类似于`ct`,但它会排除两个合数涉及的所有质因数,并计算剩余质数的组合数量,然后乘以这两个合数的出现次数的乘积。这两个函数都是通过筛选和组合质数的方式,来计算在特定条件下的子集可能性。
🦆
最终结果使用了模运算`(10**9+7)`,为什么选择这个数作为模数?
模数`10**9+7`是一个常用的大质数,在竞赛和算法实现中广泛使用,主要是因为它足够大,可以防止计算结果在整数运算中溢出,同时又能保持计算的高效性。此外,由于它是质数,也便于在取模运算中保持数的分布均匀,减少潜在的冲突或偏差。使用此模数是为了保证结果的稳定性和正确性,同时符合编程实践中的标准。

相关问题