LC 1994. 好子集的数目
https://leetcode-cn.com/problems/the-number-of-good-subsets/
这题的nums的数量非常多,但是范围却非常小,所以从范围入手是个好主意,对于重复出现的数字其实性质是一样的。
从1-30范围内的素数以及符合基本条件的数字也就是下面这么多
- primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
- good_numbers = [2, 3, 5, 6, 7, 10, 11, 13, 14, 15, 17, 19, 21, 22, 23, 26, 29, 30] # size = 18
我们可以把每个 good_numbers 里面的数字挂在对应的素数下面,那么这个组合的情况其实不多。
实现中还有个问题要考虑的是,比如15如果挂在了3下面就不要挂在5下面,不然在选择的时候会出现
- 在3下面选择15,在5下面不选择
- 在3下面不选择,在5下面选择15
出现重复计算的情况,所以我们选择某一个prime进行挂载就好了。
还可能出现个问题是,比如对于7和14,如果14挂在了2下面,而7挂在了7下面,他们之间还是可能有公约数的情况,所以在搜索的时候, 我们可能还是需要判断,选择某个数是否和之前的选择数是互质的。因为范围比较小,所以这个gcd可以预先打表计算出来。
最后我们要把1排除在外,对于每个1有选择或者是不选择两种情况,所以结果需要乘以 `1<<(cnt[1])`
class Solution: def numberOfGoodSubsets(self, nums: List[int]) -> int: primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] good_numbers = [2, 3, 5, 6, 7, 10, 11, 13, 14, 15, 17, 19, 21, 22, 23, 26, 29, 30] coprimes = set() def gcd(x, y): while y != 0: x, y = y, x % y return x for x in range(1, 31): for y in range(1, 31): if gcd(x, y) == 1: coprimes.add((x, y)) cnt = [0] * 31 for x in nums: cnt[x] += 1 counters = [] for p in primes: tmp = [] for x in good_numbers: if x % p == 0 and cnt[x] != 0: tmp.append((x, cnt[x])) cnt[x] = 0 # not select anymore counters.append(tmp) def dfs(k, select): if k == len(primes): return 1 res = dfs(k + 1, select) for x, c in counters[k]: ok = True # for p in select: # if (x, p) not in coprimes: # ok = False # break if ok: select.append(x) res += c * dfs(k + 1, select) select.pop() return res # print(counters) # including empty set. ans = dfs(0, []) - 1 ans *= (1 << cnt[1]) MOD = 10 ** 9 + 7 return ans % MOD