题目:
给你一个正整数数组 nums 。
如果数组 nums 的子集中的元素乘积是一个 无平方因子数 ,则认为该子集是一个 无平方 子集。
无平方因子数 是无法被除 1 之外任何平方数整除的数字。
返回数组 nums 中 无平方 且 非空 的子集数目。因为答案可能很大,返回对 109 + 7 取余的结果。
nums 的 非空子集 是可以由删除 nums 中一些元素(可以不删除,但不能全部删除)得到的一个数组。如果构成两个子集时选择删除的下标不同,则认为这两个子集不同。
示例 1:
输入:nums = [3,4,4,5]
输出:3
解释:示例中有 3 个无平方子集:
- 由第 0 个元素 [3] 组成的子集。其元素的乘积是 3 ,这是一个无平方因子数。
- 由第 3 个元素 [5] 组成的子集。其元素的乘积是 5 ,这是一个无平方因子数。
- 由第 0 个和第 3 个元素 [3,5] 组成的子集。其元素的乘积是 15 ,这是一个无平方因子数。
可以证明给定数组中不存在超过 3 个无平方子集。
示例 2:
输入:nums = [1]
输出:1
解释:示例中有 1 个无平方子集:
- 由第 0 个元素 [1] 组成的子集。其元素的乘积是 1 ,这是一个无平方因子数。
可以证明给定数组中不存在超过 1 个无平方子集。
提示:
1 <= nums.length <= 1000
1 <= nums[i] <= 30
java代码:
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, MX = 30, N_PRIMES = PRIMES.length, M = 1 << N_PRIMES;
private static final int[] SF_TO_MASK = new int[MX + 1]; // SF_TO_MASK[i] 为 i 的质因子集合(用二进制表示)
static {
for (int i = 2; i <= MX; ++i)
for (int j = 0; j < N_PRIMES; ++j) {
int p = PRIMES[j];
if (i % p == 0) {
if (i % (p * p) == 0) { // 有平方因子
SF_TO_MASK[i] = -1;
break;
}
SF_TO_MASK[i] |= 1 << j; // 把 j 加到集合中
}
}
}
public int squareFreeSubsets(int[] nums) {
var f = new int[M]; // f[j] 表示恰好组成质数集合 j 的方案数
f[0] = 1; // 质数集合是空集的方案数为 1
for (int x : nums) {
int mask = SF_TO_MASK[x];
if (mask >= 0) // x 是 SF
for (int j = M - 1; j >= mask; --j)
if ((j | mask) == j) // mask 是 j 的子集
f[j] = (f[j] + f[j ^ mask]) % MOD; // 不选 mask + 选 mask
}
var ans = 0L;
for (int v : f) ans += v;
return (int) ((ans - 1) % MOD); // -1 去掉空集(nums 的空子集)
}
}
网友评论