开启掘金成长之旅!这是我参与「掘金日新计划 · 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 ,可以表示为互不相同的质数 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 的乘积。
提示:
1 <= nums.length <= 1051 <= nums[i] <= 30
解题思路:
- 包含多个相同质因子的数(比如4 = 2 × 2),无法使用,直接排除;
- 1-30 质数一共只有10个,用一个10位二进制数字的每一位代表一个质数,1代表存在,0代表不存在;
- 可以用这个二进制数表示出的集合,都是有效的“好”集合,因为他们的质因数最多只有1个;
- 可以知道全部好子集类型的数目为2^10个;
- 我们统计原数组中各有效数字的数量,保存在cnt中,每次从中取出一个数字更改状态。初始时,只有全0状态数目为1,其余全部状态数目都是0;
- 添加一个数字k后,它与添加之前某状态s的&结果如果是0,则说明与之前的s状态不冲突,可以与它组成新有效集合s | i,新集合新增的数目为cnt[i] * dp[s];
- 计算完成后,遍历全部状态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));
};
最后
如果有更好的解法或者思路, 欢迎在评论区和我交流~ ღ( ´・ᴗ・` )