列表

详情


1994. 好子集的数目

给你一个整数数组 nums 。如果 nums 的一个子集中,所有元素的乘积可以表示为一个或多个 互不相同的质数 的乘积,那么我们称它为 好子集 。

请你返回 nums 中不同的  子集的数目对 109 + 7 取余 的结果。

nums 中的 子集 是通过删除 nums 中一些(可能一个都不删除,也可能全部都删除)元素后剩余元素组成的数组。如果两个子集删除的下标不同,那么它们被视为不同的子集。

 

示例 1:

输入:nums = [1,2,3,4]
输出:6
解释:好子集为:
- [1,2]:乘积为 2 ,可以表示为质数 2 的乘积。
- [1,2,3]:乘积为 6 ,可以表示为互不相同的质数 2 和 3 的乘积。
- [1,3]:乘积为 3 ,可以表示为质数 3 的乘积。
- [2]:乘积为 2 ,可以表示为质数 2 的乘积。
- [2,3]:乘积为 6 ,可以表示为互不相同的质数 2 和 3 的乘积。
- [3]:乘积为 3 ,可以表示为质数 3 的乘积。

示例 2:

输入:nums = [4,2,3,15]
输出:5
解释:好子集为:
- [2]:乘积为 2 ,可以表示为质数 2 的乘积。
- [2,3]:乘积为 6 ,可以表示为互不相同质数 2 和 3 的乘积。
- [2,15]:乘积为 30 ,可以表示为互不相同质数 2,3 和 5 的乘积。
- [3]:乘积为 3 ,可以表示为质数 3 的乘积。
- [15]:乘积为 15 ,可以表示为互不相同质数 3 和 5 的乘积。

 

提示:

原站题解

去查看

上次编辑到这里,代码来自缓存 点击恢复默认模板
class Solution { public: int numberOfGoodSubsets(vector<int>& nums) { } };

java 解法, 执行用时: 55 ms, 内存消耗: 57.7 MB, 提交时间: 2023-06-12 09:59:43

class Solution {
    private static final int[] PRIMES = new int[]{2, 3, 5, 7, 11, 13, 17, 19, 23, 29};
    private static final int MOD = (int)1e9 + 7;
    private static final Set<Integer> FORBID = new HashSet<>();
    private static final int LEN_POWER = 1 << PRIMES.length;
    static {
        for(int i = 0; i < 3; i++)
            for(int j = PRIMES[i] * PRIMES[i]; j <= 30; j += PRIMES[i] * PRIMES[i])
                FORBID.add(j);
    }

    public int numberOfGoodSubsets(int[] nums) {
        Map<Integer, Integer> cnts = new HashMap<>();
        for(int num: nums)
            if(!FORBID.contains(num))
                cnts.put(num, cnts.getOrDefault(num, 0) + 1);
        Map<Integer, Integer> keyPrimes = new HashMap<>();
        for(int num: cnts.keySet()) {
            int cur = 0;
            for(int i = 0; i < PRIMES.length; i++)
                if(num % PRIMES[i] == 0)
                    cur |= 1 << i;
            keyPrimes.put(num, cur);
        }
        int[] dp = new int[LEN_POWER];
        dp[0] = qpow(2, cnts.getOrDefault(1, 0), (long)MOD);
        for(int i = 2; i <= 30; i++) {
            if(cnts.containsKey(i))
                for(int j = LEN_POWER - 1; j > 0; j--) {
                    int cur = keyPrimes.get(i);
                    if((cur & j) == cur) {
                        dp[j] = (dp[j] + (int)((long)cnts.get(i) * (long)dp[j ^ cur] % MOD)) % MOD;
                    }
                }
        }
        int ans = 0;
        for(int j = 1; j < LEN_POWER; j++)
            ans = (ans + dp[j]) % MOD;
        return ans;
    }

    int qpow(int a, int n, long mod){
        long ans = 1, la = (long)a;
        while(n > 0){
            if((n&1) == 1)
                ans = ans * la % mod;
            la = la * la % MOD;
            n >>= 1;
        }
        return (int)ans;
    }
}

python3 解法, 执行用时: 544 ms, 内存消耗: 31.8 MB, 提交时间: 2023-06-12 09:59:13

PRIMES = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
FORBID = [p * p for p in PRIMES[:3]]
MOD = int(1e9 + 7)
class Solution:
    def numberOfGoodSubsets(self, nums: List[int]) -> int:
        cnts = Counter(nums)
        for k in list(cnts.keys()):
            for f in FORBID:
                if not k % f:
                    cnts.pop(k)
        key_primes = defaultdict(set)
        for k in cnts:
            for p in PRIMES:
                if not k % p:
                    key_primes[k].add(p)
                elif k < p:
                    break
        ones = pow(2, cnts[1], MOD)
        
        @lru_cache(None)
        def dfs(idx, ps):
            if idx > 30 or len(ps) == len(PRIMES):
                # 不可能所有质数都没被选的空集
                return (len(ps) > 0) * ones
            st, ans = set(ps), 0
            # idx存在在原数组中且质因子尚未被选
            if idx in cnts and not key_primes[idx] & st:
                ans += cnts[idx] % MOD * dfs(idx + 1, tuple(st | key_primes[idx])) % MOD
            # 叠加不选idx的答案数
            return (ans + dfs(idx + 1, ps)) % MOD

        return dfs(2, tuple())

golang 解法, 执行用时: 304 ms, 内存消耗: 8.2 MB, 提交时间: 2023-06-12 09:58:49

var primes = []int{2, 3, 5, 7, 11, 13, 17, 19, 23, 29}

func numberOfGoodSubsets(nums []int) (ans int) {
    const mod int = 1e9 + 7
    freq := [31]int{}
    for _, num := range nums {
        freq[num]++
    }

    f := make([]int, 1<<len(primes))
    f[0] = 1
    for i := 0; i < freq[1]; i++ {
        f[0] = f[0] * 2 % mod
    }
next:
    for i := 2; i < 31; i++ {
        if freq[i] == 0 {
            continue
        }

        // 检查 i 的每个质因数是否均不超过 1 个
        subset := 0
        for j, prime := range primes {
            if i%(prime*prime) == 0 {
                continue next
            }
            if i%prime == 0 {
                subset |= 1 << j
            }
        }

        // 动态规划
        for mask := 1 << len(primes); mask > 0; mask-- {
            if mask&subset == subset {
                f[mask] = (f[mask] + f[mask^subset]*freq[i]) % mod
            }
        }
    }

    for _, v := range f[1:] {
        ans = (ans + v) % mod
    }
    return
}

python3 解法, 执行用时: 288 ms, 内存消耗: 20.9 MB, 提交时间: 2023-06-12 09:58:27

'''
定义 f[i][mask] 表示当我们只选择 [2,i] 范围内的数,
并且选择的数的质因数使用情况为 mask 时的方案数。
如果 i 本身包含平方因子,那么我们无法选择 i,
相当于在 [2,i−1] 范围内选择,状态转移方程为:f[i][mask]=f[i−1][mask]
如果 i 本身不包含平方因子,记其包含的质因子的二进制表示为 subset(同样可以通过试除的方法得到),
那么状态转移方程为:
f[i][mask]=f[i−1][mask]+f[i−1][mask\subset]×freq[i]


freq[i] 表示数组 nums 中 i 出现的次数;
mask\subset 表示从二进制表示 mask 中去除所有在 subset 中出现的 1,
可以使用按位异或运算实现。
'''
class Solution:
    def numberOfGoodSubsets(self, nums: List[int]) -> int:
        primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
        mod = 10**9 + 7

        freq = Counter(nums)
        f = [0] * (1 << len(primes))
        f[0] = pow(2, freq[1], mod)

        for i, occ in freq.items():
            if i == 1:
                continue
            
            # 检查 i 的每个质因数是否均不超过 1 个
            subset, x = 0, i
            check = True
            for j, prime in enumerate(primes):
                if x % (prime * prime) == 0:
                    check = False
                    break
                if x % prime == 0:
                    subset |= (1 << j)
            
            if not check:
                continue

            # 动态规划
            for mask in range((1 << len(primes)) - 1, 0, -1):
                if (mask & subset) == subset:
                    f[mask] = (f[mask] + f[mask ^ subset] * occ) % mod

        ans = sum(f[1:]) % mod
        return ans

上一题