Subsets with distinct prime factors

Last Updated : 26 May, 2026

Given an array arr[] of size n. The task is to find a number of subsets whose product can be represented as a product of one or more distinct prime numbers. Modify your answer to the modulo of 109 + 7.

Constraints:

  • 1 <= arr.size() <= 105
  • 1< = arr[i] <= 30

Example:

Input: arr[] = [1, 2, 3, 4]
Output: 6
Explanation: The good subsets are:

  • [1, 2]: product is 2, which is the product of distinct prime 2.
  • [1, 2, 3]: product is 6, which is the product of distinct primes 2 and 3.
  • [1, 3]: product is 3, which is the product of distinct prime 3.
  • [2]: product is 2, which is the product of distinct prime 2.
  • [2, 3]: product is 6, which is the product of distinct primes 2 and 3.
  • [3]: product is 3, which is the product of distinct prime 3.

Input: arr[] = [2, 2, 3]
Output: 5
Explanation: The good subsets are : [2], [2], [2, 3], [2, 3], [3]

Try It Yourself
redirect icon

[Backtracking Approach] Using Recursion – O(2ⁿ × √m) Time and O(n) Space

The idea is to generate all possible subsets using recursion and backtracking. A subset is considered good only if no prime factor repeats and every selected number is square-free. For each element, we either include it in the subset or skip it, while tracking already used prime factors.

  • Recursively try both choices for every element: take or skip
  • Before taking a number, check whether it is square-free
  • Extract distinct prime factors of the current number
  • If any prime factor is already used, skip that number
  • Otherwise, mark primes as used, continue recursion, and backtrack later
  • Count only non-empty valid subsets
C++
#include <bits/stdc++.h>
using namespace std;

// Check whether number is square free or not
// Example:
// 6  -> valid
// 12 -> invalid because 2 repeats
bool validNumber(int num)
{
    for (int i = 2; i * i <= num; i++) {

        int cnt = 0;

        while (num % i == 0) {
            cnt++;
            num /= i;

            // repeated prime factor found
            if (cnt > 1)
                return false;
        }
    }

    return true;
}

// Store distinct prime factors
vector<int> getPrimeFactors(int num)
{
    vector<int> primes;

    for (int i = 2; i * i <= num; i++) {

        if (num % i == 0) {

            primes.push_back(i);

            while (num % i == 0)
                num /= i;
        }
    }

    // remaining prime number
    if (num > 1)
        primes.push_back(num);

    return primes;
}

int solve(int idx,
          vector<int>& nums,
          map<int,int>& usedPrime,
          bool taken)
{
    // all elements processed
    if (idx == nums.size()) {

        // count only non empty subsets
        if (taken)
            return 1;

        return 0;
    }

    // option 1 -> skip current element
    int notTake =
        solve(idx + 1,
              nums,
              usedPrime,
              taken);

    int take = 0;

    int num = nums[idx];

    // invalid number can never be used
    if (!validNumber(num))
        return notTake;

    vector<int> primes = getPrimeFactors(num);

    bool possible = true;

    // check whether any prime already used
    for (int p : primes) {

        if (usedPrime[p]) {
            possible = false;
            break;
        }
    }

    // option 2 -> take current element
    if (possible) {

        // mark primes as used
        for (int p : primes)
            usedPrime[p]++;

        take =
            solve(idx + 1,
                  nums,
                  usedPrime,
                  true);

        // backtracking step
        for (int p : primes)
            usedPrime[p]--;
    }

    return take + notTake;
}

int goodSubsets(vector<int>& nums)
{
    map<int,int> usedPrime;

    return solve(0, nums, usedPrime, false);
}

int main()
{
    vector<int> arr = {1, 2, 3, 4};

    cout << goodSubsets(arr);

    return 0;
}
Java
import java.util.*;

// Check whether number is square free or not
// Example:
// 6  -> valid
// 12 -> invalid because 2 repeats

class GFG {

    static boolean validNumber(int num)
    {
        for (int i = 2; i * i <= num; i++) {

            int cnt = 0;

            while (num % i == 0) {
                cnt++;
                num /= i;

                // repeated prime factor found
                if (cnt > 1)
                    return false;
            }
        }

        return true;
    }

    // Store distinct prime factors
    static ArrayList<Integer> getPrimeFactors(int num)
    {
        ArrayList<Integer> primes = new ArrayList<>();

        for (int i = 2; i * i <= num; i++) {

            if (num % i == 0) {

                primes.add(i);

                while (num % i == 0)
                    num /= i;
            }
        }

        // remaining prime number
        if (num > 1)
            primes.add(num);

        return primes;
    }

    static int solve(int idx, int[] nums,
                     HashMap<Integer, Integer> usedPrime,
                     boolean taken)
    {
        // all elements processed
        if (idx == nums.length) {

            // count only non empty subsets
            if (taken)
                return 1;

            return 0;
        }

        // option 1 -> skip current element
        int notTake
            = solve(idx + 1, nums, usedPrime, taken);

        int take = 0;

        int num = nums[idx];

        // invalid number can never be used
        if (!validNumber(num))
            return notTake;

        ArrayList<Integer> primes = getPrimeFactors(num);

        boolean possible = true;

        // check whether any prime already used
        for (int p : primes) {

            if (usedPrime.getOrDefault(p, 0) > 0) {
                possible = false;
                break;
            }
        }

        // option 2 -> take current element
        if (possible) {

            // mark primes as used
            for (int p : primes)
                usedPrime.put(
                    p, usedPrime.getOrDefault(p, 0) + 1);

            take = solve(idx + 1, nums, usedPrime, true);

            // backtracking step
            for (int p : primes)
                usedPrime.put(p, usedPrime.get(p) - 1);
        }

        return take + notTake;
    }

    static int goodSubsets(int[] nums)
    {
        HashMap<Integer, Integer> usedPrime
            = new HashMap<>();

        return solve(0, nums, usedPrime, false);
    }

    public static void main(String[] args)
    {
        int[] arr = { 1, 2, 3, 4 };

        System.out.println(goodSubsets(arr));
    }
}
Python
# Check whether number is square free or not
# Example:
# 6  -> valid
# 12 -> invalid because 2 repeats
def validNumber(num):

    i = 2

    while i * i <= num:

        cnt = 0

        while num % i == 0:
            cnt += 1
            num //= i

            # repeated prime factor found
            if cnt > 1:
                return False

        i += 1

    return True


# Store distinct prime factors
def getPrimeFactors(num):

    primes = []

    i = 2

    while i * i <= num:

        if num % i == 0:

            primes.append(i)

            while num % i == 0:
                num //= i

        i += 1

    # remaining prime number
    if num > 1:
        primes.append(num)

    return primes


def solve(idx,
          nums,
          usedPrime,
          taken):

    # all elements processed
    if idx == len(nums):

        # count only non empty subsets
        if taken:
            return 1

        return 0

    # option 1 -> skip current element
    notTake = solve(idx + 1,
                    nums,
                    usedPrime,
                    taken)

    take = 0

    num = nums[idx]

    # invalid number can never be used
    if not validNumber(num):
        return notTake

    primes = getPrimeFactors(num)

    possible = True

    # check whether any prime already used
    for p in primes:

        if usedPrime.get(p, 0):
            possible = False
            break

    # option 2 -> take current element
    if possible:

        # mark primes as used
        for p in primes:
            usedPrime[p] = usedPrime.get(p, 0) + 1

        take = solve(idx + 1,
                     nums,
                     usedPrime,
                     True)

        # backtracking step
        for p in primes:
            usedPrime[p] -= 1

    return take + notTake


def goodSubsets(nums):

    usedPrime = {}

    return solve(0, nums, usedPrime, False)


arr = [1, 2, 3, 4]

print(goodSubsets(arr))
C#
using System;
using System.Collections.Generic;

// Check whether number is square free or not
// Example:
// 6  -> valid
// 12 -> invalid because 2 repeats

class GFG {

    static bool validNumber(int num)
    {
        for (int i = 2; i * i <= num; i++) {

            int cnt = 0;

            while (num % i == 0) {
                cnt++;
                num /= i;

                // repeated prime factor found
                if (cnt > 1)
                    return false;
            }
        }

        return true;
    }

    // Store distinct prime factors
    static List<int> getPrimeFactors(int num)
    {
        List<int> primes = new List<int>();

        for (int i = 2; i * i <= num; i++) {

            if (num % i == 0) {

                primes.Add(i);

                while (num % i == 0)
                    num /= i;
            }
        }

        // remaining prime number
        if (num > 1)
            primes.Add(num);

        return primes;
    }

    static int solve(int idx,
                     int[] nums,
                     Dictionary<int, int> usedPrime,
                     bool taken)
    {
        // all elements processed
        if (idx == nums.Length) {

            // count only non empty subsets
            if (taken)
                return 1;

            return 0;
        }

        // option 1 -> skip current element
        int notTake =
            solve(idx + 1,
                  nums,
                  usedPrime,
                  taken);

        int take = 0;

        int num = nums[idx];

        // invalid number can never be used
        if (!validNumber(num))
            return notTake;

        List<int> primes = getPrimeFactors(num);

        bool possible = true;

        // check whether any prime already used
        foreach (int p in primes) {

            if (usedPrime.ContainsKey(p)
                && usedPrime[p] > 0) {

                possible = false;
                break;
            }
        }

        // option 2 -> take current element
        if (possible) {

            // mark primes as used
            foreach (int p in primes) {

                if (!usedPrime.ContainsKey(p))
                    usedPrime[p] = 0;

                usedPrime[p]++;
            }

            take =
                solve(idx + 1,
                      nums,
                      usedPrime,
                      true);

            // backtracking step
            foreach (int p in primes)
                usedPrime[p]--;
        }

        return take + notTake;
    }

    static int goodSubsets(int[] nums)
    {
        Dictionary<int, int> usedPrime
            = new Dictionary<int, int>();

        return solve(0, nums, usedPrime, false);
    }

    static void Main()
    {
        int[] arr = {1, 2, 3, 4};

        Console.WriteLine(goodSubsets(arr));
    }
}
JavaScript
// Check whether number is square free or not
// Example:
// 6  -> valid
// 12 -> invalid because 2 repeats

function validNumber(num)
{
    for (let i = 2; i * i <= num; i++) {

        let cnt = 0;

        while (num % i === 0) {
            cnt++;
            num = Math.floor(num / i);

            // repeated prime factor found
            if (cnt > 1)
                return false;
        }
    }

    return true;
}

// Store distinct prime factors
function getPrimeFactors(num)
{
    let primes = [];

    for (let i = 2; i * i <= num; i++) {

        if (num % i === 0) {

            primes.push(i);

            while (num % i === 0)
                num = Math.floor(num / i);
        }
    }

    // remaining prime number
    if (num > 1)
        primes.push(num);

    return primes;
}

function solve(idx,
               nums,
               usedPrime,
               taken)
{
    // all elements processed
    if (idx === nums.length) {

        // count only non empty subsets
        if (taken)
            return 1;

        return 0;
    }

    // option 1 -> skip current element
    let notTake =
        solve(idx + 1,
              nums,
              usedPrime,
              taken);

    let take = 0;

    let num = nums[idx];

    // invalid number can never be used
    if (!validNumber(num))
        return notTake;

    let primes = getPrimeFactors(num);

    let possible = true;

    // check whether any prime already used
    for (let p of primes) {

        if (usedPrime[p]) {
            possible = false;
            break;
        }
    }

    // option 2 -> take current element
    if (possible) {

        // mark primes as used
        for (let p of primes) {

            if (!usedPrime[p])
                usedPrime[p] = 0;

            usedPrime[p]++;
        }

        take =
            solve(idx + 1,
                  nums,
                  usedPrime,
                  true);

        // backtracking step
        for (let p of primes)
            usedPrime[p]--;
    }

    return take + notTake;
}

function goodSubsets(nums)
{
    let usedPrime = {};

    return solve(0, nums, usedPrime, false);
}

let arr = [1, 2, 3, 4];

console.log(goodSubsets(arr));

Output
7

[Expected Approach] Using Bitmask DP – O(30 × 2¹⁰) Time and O(2¹⁰) Space

As per the question constraints, the array elements are in range from 1 to 30, so the idea is to represent the prime factors of every number using a bitmask. Each bit corresponds to a distinct prime number. Since numbers are limited from 1 to 30, only the first 10 primes are needed. A number is valid only if it is square-free. Dynamic Programming is then used to count all possible good subsets while ensuring no prime factor overlaps.

  • Count frequency of every number from 1 to 30
  • Generate a prime-factor bitmask for each number
  • Ignore numbers having repeated prime factors
  • Use dp[mask] where mask represents used prime factors
  • Traverse masks in reverse to avoid overwriting current states
  • Add current number only if its mask does not overlap with the existing mask
  • Sum all valid states and subtract the empty subset
C++
#include <bits/stdc++.h>
using namespace std;

// Create prime mask for current number
int getMask(int num)
{
    vector<int> primes = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29};

    int mask = 0;

    for (int i = 0; i < 10; i++)
    {
        int p = primes[i];
        int cnt = 0;
        while (num % p == 0)
        {
            cnt++;
            num /= p;
        }

        // repeated prime factor
        // invalid number
        if (cnt > 1)
            return -1;

        // store current prime in mask
        if (cnt == 1)
            mask |= (1 << i);
    }

    return mask;
}

int goodSubsets(vector<int> &nums)
{
    int n = nums.size();

    // frequency of every number
    vector<int> freq(31, 0);

    for (int x : nums)
        freq[x]++;

    // dp[mask]
    // mask represents used prime factors
    vector<int> dp(1024, 0);

    // empty subset
    dp[0] = 1;

    // process numbers from 2 to 30
    for (int num = 2; num <= 30; num++)
    {

        // number not present
        if (freq[num] == 0)
            continue;

        int currMask = getMask(num);

        // invalid number
        if (currMask == -1)
            continue;

        // reverse traversal
        // prevents overwriting current states
        for (int mask = 1023; mask >= 0; mask--)
        {

            // overlapping prime factor
            // cannot take together
            if ((mask & currMask) != 0)
                continue;

            dp[mask | currMask] += dp[mask] * freq[num];
        }
    }

    int ans = 0;

    // add all good subsets
    for (int x : dp)
        ans += x;

    // remove empty subset
    return ans - 1;
}

int main()
{
    vector<int> arr = {1, 2, 3, 4};
    cout << goodSubsets(arr);
    return 0;
}
Java
import java.util.*;

class GFG {

    // Create prime mask for current number
    static int getMask(int num)
    {
        int[] primes
            = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 };

        int mask = 0;

        for (int i = 0; i < 10; i++) {

            int p = primes[i];

            int cnt = 0;

            while (num % p == 0) {
                cnt++;
                num /= p;
            }

            // repeated prime factor
            // invalid number
            if (cnt > 1)
                return -1;

            // store current prime in mask
            if (cnt == 1)
                mask |= (1 << i);
        }

        return mask;
    }

    static int goodSubsets(int[] nums)
    {
        int n = nums.length;

        // frequency of every number
        int[] freq = new int[31];

        for (int x : nums)
            freq[x]++;

        // dp[mask]
        // mask represents used prime factors
        int[] dp = new int[1024];

        // empty subset
        dp[0] = 1;

        // process numbers from 2 to 30
        for (int num = 2; num <= 30; num++) {

            // number not present
            if (freq[num] == 0)
                continue;

            int currMask = getMask(num);

            // invalid number
            if (currMask == -1)
                continue;

            // reverse traversal
            // prevents overwriting current states
            for (int mask = 1023; mask >= 0; mask--) {

                // overlapping prime factor
                // cannot take together
                if ((mask & currMask) != 0)
                    continue;

                dp[mask | currMask] += dp[mask] * freq[num];
            }
        }

        int ans = 0;

        // add all good subsets
        for (int x : dp)
            ans += x;

        // remove empty subset
        return ans - 1;
    }

    public static void main(String[] args)
    {
        int[] arr = { 1, 2, 3, 4 };

        System.out.println(goodSubsets(arr));
    }
}
Python
# Create prime mask for current number
def getMask(num):

    primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]

    mask = 0

    for i in range(10):

        p = primes[i]

        cnt = 0

        while num % p == 0:
            cnt += 1
            num //= p

        # repeated prime factor
        # invalid number
        if cnt > 1:
            return -1

        # store current prime in mask
        if cnt == 1:
            mask |= (1 << i)

    return mask


def goodSubsets(nums):

    n = len(nums)

    # frequency of every number
    freq = [0] * 31

    for x in nums:
        freq[x] += 1

    # dp[mask]
    # mask represents used prime factors
    dp = [0] * 1024

    # empty subset
    dp[0] = 1

    # process numbers from 2 to 30
    for num in range(2, 31):

        # number not present
        if freq[num] == 0:
            continue

        currMask = getMask(num)

        # invalid number
        if currMask == -1:
            continue

        # reverse traversal
        # prevents overwriting current states
        for mask in range(1023, -1, -1):

            # overlapping prime factor
            # cannot take together
            if (mask & currMask) != 0:
                continue

            dp[mask | currMask] += \
                dp[mask] * freq[num]

    ans = 0

    # add all good subsets
    for x in dp:
        ans += x

    # remove empty subset
    return ans - 1


arr = [1, 2, 3, 4]

print(goodSubsets(arr))
C#
using System;

class GFG {

    // Create prime mask for current number
    static int getMask(int num)
    {
        int[] primes
            = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 };

        int mask = 0;

        for (int i = 0; i < 10; i++) {

            int p = primes[i];

            int cnt = 0;

            while (num % p == 0) {
                cnt++;
                num /= p;
            }

            // repeated prime factor
            // invalid number
            if (cnt > 1)
                return -1;

            // store current prime in mask
            if (cnt == 1)
                mask |= (1 << i);
        }

        return mask;
    }

    static int goodSubsets(int[] nums)
    {
        int n = nums.Length;

        // frequency of every number
        int[] freq = new int[31];

        foreach(int x in nums) freq[x]++;

        // dp[mask]
        // mask represents used prime factors
        int[] dp = new int[1024];

        // empty subset
        dp[0] = 1;

        // process numbers from 2 to 30
        for (int num = 2; num <= 30; num++) {

            // number not present
            if (freq[num] == 0)
                continue;

            int currMask = getMask(num);

            // invalid number
            if (currMask == -1)
                continue;

            // reverse traversal
            // prevents overwriting current states
            for (int mask = 1023; mask >= 0; mask--) {

                // overlapping prime factor
                // cannot take together
                if ((mask & currMask) != 0)
                    continue;

                dp[mask | currMask] += dp[mask] * freq[num];
            }
        }

        int ans = 0;

        // add all good subsets
        foreach(int x in dp) ans += x;

        // remove empty subset
        return ans - 1;
    }

    static void Main()
    {
        int[] arr = { 1, 2, 3, 4 };

        Console.WriteLine(goodSubsets(arr));
    }
}
JavaScript
// Create prime mask for current number
function getMask(num)
{
    let primes = [ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 ];

    let mask = 0;

    for (let i = 0; i < 10; i++) {

        let p = primes[i];

        let cnt = 0;

        while (num % p === 0) {
            cnt++;
            num = Math.floor(num / p);
        }

        // repeated prime factor
        // invalid number
        if (cnt > 1)
            return -1;

        // store current prime in mask
        if (cnt === 1)
            mask |= (1 << i);
    }

    return mask;
}

function goodSubsets(nums)
{
    let n = nums.length;

    // frequency of every number
    let freq = new Array(31).fill(0);

    for (let x of nums)
        freq[x]++;

    // dp[mask]
    // mask represents used prime factors
    let dp = new Array(1024).fill(0);

    // empty subset
    dp[0] = 1;

    // process numbers from 2 to 30
    for (let num = 2; num <= 30; num++) {

        // number not present
        if (freq[num] === 0)
            continue;

        let currMask = getMask(num);

        // invalid number
        if (currMask === -1)
            continue;

        // reverse traversal
        // prevents overwriting current states
        for (let mask = 1023; mask >= 0; mask--) {

            // overlapping prime factor
            // cannot take together
            if ((mask & currMask) !== 0)
                continue;

            dp[mask | currMask] += dp[mask] * freq[num];
        }
    }

    let ans = 0;

    // add all good subsets
    for (let x of dp)
        ans += x;

    // remove empty subset
    return ans - 1;
}

let arr = [ 1, 2, 3, 4 ];

console.log(goodSubsets(arr));

Output
3


Comment