LeetCode Everyday - 好子集的数目

109 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第4天,点击查看活动详情

好子集的数目

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

比方说,如果 nums = [1, 2, 3, 4] : [2, 3] ,[1, 2, 3] 和 [1, 3] 是 好 子集,乘积分别为 6 = 23 ,6 = 23 和 3 = 3 。 [1, 4] 和 [4] 不是 好 子集,因为乘积分别为 4 = 22 和 4 = 22 。 请你返回 nums 中不同的 好 子集的数目对 109 + 7 取余 的结果。

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

示例1:

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

示例2:

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

提示:

  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 30

解题思路:

  1. 包含多个相同质因子的数(比如4 = 2 × 2),无法使用,直接排除;
  2. 1-30 质数一共只有10个,用一个10位二进制数字的每一位代表一个质数,1代表存在,0代表不存在;
  3. 可以用这个二进制数表示出的集合,都是有效的“好”集合,因为他们的质因数最多只有1个;
  4. 可以知道全部好子集类型的数目为2^10个;
  5. 我们统计原数组中各有效数字的数量,保存在cnt中,每次从中取出一个数字更改状态。初始时,只有全0状态数目为1,其余全部状态数目都是0;
  6. 添加一个数字k后,它与添加之前某状态s的&结果如果是0,则说明与之前的s状态不冲突,可以与它组成新有效集合s | i,新集合新增的数目为cnt[i] * dp[s];
  7. 计算完成后,遍历全部状态s,除全空状态dp[0]外,其他所有dp[s]的加和就是所求数量。

我的答案:

/**
 * @param {number[]} nums
 * @return {number}
 */
var numberOfGoodSubsets = function(nums) {
    // 2, 3, 5, 7, 11, 13, 17, 19, 23, 29
    // 0  1  2  3  4  5  6  7  8   9   10
    let primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29];
    let states = new Map();
    let cnt = new Map();
    let ones = 0;
    const mod = 10 ** 9 + 7;
    
    function getState(e) {
        if (primes.includes(e)) {
            return (1 << primes.indexOf(e));
        }
        for (let i of primes) {
            let c = e / i;
            if (primes.includes(c) && c !== i) {
                return ((1 << primes.indexOf(c)) | (1 << primes.indexOf(i)));
            }
        }
        if (e === 30) {
            return ((1 << 0) | (1 << 1) | (1 << 2));
        }
        return -1;
    }

    for (let i of nums) {
        if (i === 1) {
            ones += 1;
            continue;
        }
        let state = getState(i);
        if (state !== -1) {
            cnt.has(i) ? cnt.set(i, cnt.get(i)+1) : cnt.set(i, 1);
            states.set(i, state);
        }
    }

    let dp = new Array(2 ** 10 ).fill(0);
    dp [0] = 1;
    for (let k of cnt.keys()) {
        let i = states.get(k);
        for (let s=0; s<(1 << 10); s++) {
            if ((s & i) === 0) {
                dp[(s|i)] += cnt.get(k) * dp[s];
                dp[(s|i)] %= mod;
            }
        }
    }
    let res = 0n;
    for (let i=1; i<(1 << 10); i++) {
        res += BigInt(dp[i]);
    }
    return Number(res * (2n ** BigInt(ones)) % BigInt(mod));
};

最后

如果有更好的解法或者思路, 欢迎在评论区和我交流~ ღ( ´・ᴗ・` )