1+ from typing import List , Union , Collection , Mapping , Optional
2+ from collections import defaultdict
3+
4+ class Solution :
5+ def countTheNumOfKFreeSubsets (self , nums : List [int ], k : int ) -> int :
6+ """
7+ Count k-Free subsets using dynamic programming.
8+
9+ Approach:
10+ 1. Group elements by (num % k) to find independent groups
11+ 2. Within each group, sort and build chains where elements differ by k
12+ 3. For each chain, use House Robber DP to count valid subsets
13+ 4. Multiply results across all independent chains
14+
15+ Time: O(n log n), Space: O(n)
16+ """
17+ # Group numbers by their remainder when divided by k
18+ groups = defaultdict (list )
19+ for num in nums :
20+ groups [num % k ].append (num )
21+
22+ res = 1
23+
24+ # Process each group independently
25+ for group in groups .values ():
26+ group .sort ()
27+
28+ # Build chains within this group
29+ i = 0
30+ while i < len (group ):
31+ chain = [group [i ]]
32+ j = i + 1
33+
34+ # Build chain where each element is exactly k more than previous
35+ while j < len (group ) and group [j ] == chain [- 1 ] + k :
36+ chain .append (group [j ])
37+ j += 1
38+
39+ # House Robber DP for this chain
40+ m = len (chain )
41+ if m == 1 :
42+ chain_res = 2 # {} or {chain[0]}
43+ else :
44+ take = 1 # Take first element
45+ skip = 1 # Skip first element
46+
47+ for idx in range (1 , m ):
48+ new_take = skip # Can only take current if we skipped previous
49+ new_skip = take + skip # Can skip current regardless
50+ take , skip = new_take , new_skip
51+
52+ chain_res = take + skip
53+
54+ res *= chain_res
55+ i = j
56+
57+ return res
0 commit comments