题目描述
给你一个整数数组 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 <= 105
- 1 <= nums[i] <= 30
题解思路
题目给定好子集的定义以及范围【1,30】
对于任意的好子集我们添加任意数目的1 其仍为好子集
而且
- 2,3,5,6,7,10,11,13,14,15,17,19,21,22,23,26,29,30:这些数均不包含平方因子,因此每个数在好子集中至多出现一次;
- 4,8,9,12,16,18,20,24,25,27,28 这些数包含平方因子,因此一定不能在好子集中出现。
我们将【1,30】内的质数预先处理出来为
- 2,3,5,7,11,13,17,19,23,29
因为每个质数只能出现一次 我们可以用一个长度为10的二进制数mask来表示这些质因数的使用情况
- 其中mask[i]为1表示第i个质数已经使用过
定义 f[i][mask] 表示当我们只选择 [2, i]范围内的数,并且选择的数的质因数使用情况为mask 时的方案数
当我们选择的数i包含平方因子的时候 则状态转移方程为
- f[i][mask] = f[i-1][mask]
如果i不包含平方因子的时候 状态转移方程为
- 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
subset,x=0,i
check=True
for j,num in enumerate(primes):
if x%(num*num)==0:
check=False
break
if x%num==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