[Daily question] 1994. The number of good subsets

Topic Source: LeetCode
Original Link: https://mp.weixin.qq.com/s/myI7_ZwJM7kizrwUtWgAZQ
Difficulty Level: Difficult

topic description

You are given an integer array nums. If the product of all elements in a subset of nums can be expressed as the product of one or more prime numbers that are different from each other, then we call it a good subset .

  • Let's say if nums = [1, 2, 3, 4] :
  • [2, 3] , [1, 2, 3] and [1, 3] are good subsets, the product is 6 = 2 × 3 6 = 2 \times 36=2×3 6 = 2 × 3 6 = 2 \times 3 6=2×3 and 3 = 3.
  • [1, 4] and [4] are not good subsets because the product is 4 = 2 × 2 4 = 2 \times 24=2×2 and4 = 2 × 2 4 = 2 \times 24=2×2

Please return the number of different good subsets in nums to 1 0 9 + 7 10^9 + 7109+7 Take the remainder of the result.

A subset in nums is an array of remaining elements after deleting some (maybe none or all) elements in nums. Two subsets are considered different subsets if their subscripts for deletion differ.

Example 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 的乘积。

Example 2:

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

hint:

  • 1 < = n u m s . l e n g t h < = 1 0 5 1 <= nums.length <= 10^5 1<=nums.length<=105
  • 1 < = n u m s [ i ] < = 30 1 <= nums[i] <= 30 1<=nums[i]<=30

Solution: state compression + dynamic programming

At first glance, the topic is clueless, but after careful reading, it is found that the number of elements in the array nums does not exceed 30, so the integers in [1,30] can be divided into the following three categories:

  • For any good subset, add any number of 1s, and the resulting new subset is still a good subset;
  • 2, 3, 5, 6, 7, 10, 11, 13, 14, 15, 17, 19, 21, 22, 23, 26, 29, 30: None of these numbers contain square factors, so each number is good occurs at most once in the subset;
  • 4, 8, 9, 12, 16, 18, 20, 24, 25, 27, 28: These numbers contain square factors and therefore must not occur in the good subset.

First of all, the integers in [1,30] are classified according to the above by hard coding, and all the prime numbers in [1,30] 2, 3, 5, 7, 11, 13, 17, 19 , 23, 29, and then dynamically classify by trial and error .

Once the classification is complete, dynamic programming can be considered. Since each prime factor can only appear once, and there are 10 prime numbers in [1, 30], a binary number mask of length 10 can be used to represent the use of these prime factors, where the i-th bit of the mask is 1 , indicating that the i-th prime number has already been used .

Therefore, define f [ i ] [ mask ] f[i][mask]f [ i ] [ ma s k ] indicates the number of schemes when only numbers in the range [2,i] are selected, and the prime factor usage of the selected number is mask.

  • If i itself contains a square factor, then i cannot be selected, which is equivalent to selecting within the range of [2, i-1], and the state transition equation is f [ i ] [ mask ] = f [ i − 1 ] [ mask ] f[i ][mask] = f[i-1][mask]f[i][mask]=f[i1][mask]
  • If i itself does not contain a square factor, record the binary representation of the prime factor it contains as a subset (which can also be obtained by the method of trial division), then the state transition equation is f [ i ] [ mask ] = f [ i − 1 ] [ mask ] + f [ i − 1 ] [ masksubset ] × freq [ i ] f[i][mask]=f[i-1][mask]+f[i-1][mask\\subset]\times freq [i]f[i][mask]=f[i1][mask]+f[i1][masksubset]×f re q [ i ] where:
    • freq[i] indicates the number of occurrences of i in the array nums;
    • mask\subset means to remove all 1s that appear in the subset from the binary representation mask, which can be realized by bitwise XOR operation. Here you need to ensure that the subset is a subset of the mask, which can be judged by bitwise AND operation.

The boundary conditions for dynamic programming are: f [ 1 ] [ 0 ] = 2 freq [ 1 ] f[1][0]=2freq[1]f[1][0]=2 f re q [ 1 ] That is, each 1 that appears in the array nums can be selected or not selected. The final answer is allf [ 30 ] [ . . ] f[30][..]f [ 30 ] [ .. ] exceptf[30][0]f[30][0]Sum of terms other than f [ 30 ] [ 0 ] .

detail
note f [ i ] [ mask ] f[i][mask]f [ i ] [ ma s k ] will only start fromf [ i − 1 ] [ . . ] f[i-1][..]f[i1 ] [ .. ] is transferred from, andf [ i − 1 ] [ . . ] f[i-1][..]f[i1 ] The subscript in [ .. ] is always smaller than the mask, so we can use a space optimization method similar to 0-1 backpack, from 2 1 0 − 1 2^10-1when traversing the mask2101 to 1 traverse in reverse order, so you only need to use a length of2 1 0 2^102The one-dimensional array of 1 0has made a state transition.

Code: Python

#!/usr/bin/env python
from collections import Counter
from typing import List


# Hard level
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

            # 检查 i 的每个质因数是否均不超过 1 个
            subset, x = 0, i
            check = True
            for j, prime in enumerate(primes):
                if x % (prime * prime) == 0:
                    check = False
                    break
                if x % prime == 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


if __name__ == '__main__':
    today = Solution()
    nums = list(map(int, input('nums = ').strip().split(',')))
    print(today.numberOfGoodSubsets(nums))

Complexity Analysis

  • Time complexity: O ( n + C × O ( 2 π ( C ) ) O(n + C × O(2π(C))O ( n+C×O ( 2 π ( C )) . Where n is the length of the array nums, C is the maximum value of nums elements, in this question C = 30, π(x) represents the number of prime numbers ≤ x.
    • A total of O ( C ) O(C) needs to be consideredO ( C ) numbers, each number requiresO ( 2 π ( C ) ) O(2π(C))O ( 2 π ( C )) time calculation dynamic programming;
    • At the beginning, it is necessary to traverse all the numbers, and the time complexity is O ( n ) O(n)O ( n )
  • Space complexity: O ( 2 π ( C ) ) O(2π(C))O ( 2 π ( C )) is the space required for dynamic programming.

Guess you like

Origin blog.csdn.net/ARPOSPF/article/details/130529577