1819. Number of Different Subsequences GCDs
Problem Description
In this problem, you are given an array of positive integers called nums
. You are asked to calculate the number of different Greatest Common Divisors (GCDs) that can be obtained from all possible non-empty subsequences of the array.
The term GCD refers to the largest integer that can evenly divide all the numbers in a sequence. For instance, the GCD of the sequence [4,6,16]
is 2
, since 2 is the greatest integer that can divide 4, 6, and 16 without leaving a remainder.
A subsequence is defined as a sequence that can be derived from the array by removing some elements (or potentially no elements at all). For example, [2,5,10]
is a subsequence of [1,2,1,2,4,1,5,10]
.
The goal is to determine how many distinct GCDs you can find among all non-empty subsequences of the nums
array.
Intuition
The solution is based on the insight that to find the GCD of a sequence, one does not necessarily need to consider all elements of the sequence; it's enough to consider only those elements that are multiples of a potential GCD candidate.
To employ this insight, we iterate over all integers x
from 1 up to the maximum number in nums
, considering each x
as a potential GCD. For each potential GCD x
, we want to find if there's a subsequence of nums whose GCD is exactly x
. We do this by iterating through all multiples of x
up to the maximum number in nums
and calculating their GCD. If at any point our calculated GCD equals x
, we know that we've found a subsequence whose GCD is x
, and we count that.
In more detail, we follow these steps:
- Find the maximum number in
nums
. This bounds the largest possible GCD. - Create a visibility set
vis
that contains all numbers present innums
to allow for O(1) lookups. - Initialize a counter
ans
to keep track of how many different GCDs we have found. - Iterate over each integer
x
starting from 1 up to the maximum number (inclusive). For eachx
, iterate through its multiples. - If a multiple
y
ofx
is invis
, calculate the GCD ofy
and the current GCD valueg
. - As soon as the GCD equals
x
, increment the counterans
and stop considering further multiples ofx
since we have found a valid subsequence for this GCD. - Return the counter
ans
, which now contains the number of distinct GCDs.
By doing this, we systematically check each number as a potential GCD and use the presence of its multiples in nums
to see if that number is the GCD of some subsequence. With this approach, we are able to arrive at the solution efficiently.
Learn more about Math patterns.
Solution Approach
The implementation of the solution uses the mathematical concept of the Greatest Common Divisor (GCD) and some basic set operations for efficient lookup. The general approach involves iterating over all possible GCD values, checking for their existence by examining the multiples within the given nums
array, and calculating the actual GCD. Here is a deeper dive into the approach:
-
Calculate the Maximum Value (
mx
): Determine the maximum value in thenums
array, which sets a bound for the largest possible GCD. -
Visibility Set (
vis
): Convert thenums
array into a set. Thisvis
set allows for constant-time complexity (O(1)
) checks to determine if a multiple of the current GCD candidatex
is in thenums
array. -
GCD Function: The built-in
gcd
function from Python's[math](/problems/math-basics)
module is used to compute the Greatest Common Divisor of two numbers. -
Iterate Over Potential GCDs (
x
): For every integerx
from 1 tomx
, perform the following:- Initialize a variable
g
to 0, which will store the current GCD as we iterate through the multiples ofx
. - Loop through each multiple of
x
—y
—starting fromx
tomx
(inclusive) and incremented byx
. We only need to consider multiples ofx
because a GCD will always have to be a factor of the numbers in the subsequence.
- Initialize a variable
-
Check Multiples and Calculate GCD:
- If the multiple
y
is present in thevis
set, compute the GCD of the current GCD valueg
andy
. This GCD represents the GCD of the subsequence consisting of the multiples ofx
encountered so far. As such,g
is incrementally updated to always reflect the GCD of this growing subsequence. - If at any point
g
becomes equal tox
, it means we've found a subsequence whose GCD is the current candidatex
. When this happens, increment the counterans
by 1 to signify that another unique GCD has been found. - Break out of the loop for multiples of
x
because we've accomplished our goal for the current GCD candidatex
. Continuing the loop would be redundant since we only count distinct GCDs.
- If the multiple
-
Return Result: After going through all integers from 1 to
mx
, the counterans
holds the number of different GCDs. Returningans
completes the implementation and gives us the desired result.
The time complexity of the solution comes down to the number of iterations we perform, which depends on the range of numbers (mx
) and the number of divisors they have. By using a set for lookups and the efficient gcd
function, the implementation maintains acceptable performance for the given constraints.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's consider a simple example to illustrate the solution approach. We will use the array nums = [3, 6, 12]
.
Here's a step-by-step walkthrough using the provided solution approach:
-
Calculate the Maximum Value (
mx
): The maximum value in thenums
array is12
. -
Visibility Set (
vis
): Convert the array to a set:vis = {3, 6, 12}
. -
GCD Function: We will use Python's built-in
math.gcd
function to compute the Greatest Common Divisor. -
Iterate Over Potential GCDs (
x
): We iterate over each integer from1
to12
(inclusive). Let's take a few iterations as an example:- For
x = 1
:- Initialize
g = 0
. - Loop through multiples of
1
, i.e., all numbers from1
to12
. For each multiple, updateg
with the GCD value ofg
and the multiple if it exists invis
. - Since everything divides by
1
, the GCD will stay1
. - We find a subsequence:
[3]
,[6]
,[12]
, where the GCD is1
.
- Initialize
- For
x = 2
:- Initialize
g = 0
. - Loop through multiples of
2
:2
,4
,6
,8
,10
,12
. - When we reach
6
(which exists invis
), we calculate the GCD ofg
(which is0
initially) and6
, resulting in a GCD of6
. - Since
6
is not2
, we go on until we reach12
. The GCD of6
and12
is still6
. Since we never reached a GCD of2
,2
is not counted.
- Initialize
- For
x = 3
:- Initialize
g = 0
. - Loop through multiples of
3
:3
,6
,9
,12
. The first multiple we encounter is3
, which exists invis
. The GCD of0
and3
is3
. - As
g = x
, we've found a subsequence with this GCD and incrementans
by1
. - The same will hold true at multiple
6
and12
, we already haveg = 3
, so we stop checking further multiples.
- Initialize
- Continue this process for each integer
x
up to12
.
- For
-
Check Multiples and Calculate GCD: As shown in steps for
x = 1
,2
, and3
, we calculate the GCD for each multiple withinvis
, and if at any pointg
equalsx
, we count it in our answer and stop checking further multiples ofx
. -
Return Result: After iterating over all numbers from
1
to12
, we count all the unique GCDs that we found. In this example, the final count would be the GCDs obtained from all the unique subsequences, which would include1
(from every possible subsequence),3
(from[3]
,[3,6]
,[3,12]
,[3,6,12]
), and6
(from[6,12]
), giving us a total of 3 distinct GCDs.
In conclusion, the returned value for this nums
array would be 3
, as there are three different possible GCDs for the non-empty subsequences in the array.
Solution Implementation
1from math import gcd
2from typing import List
3
4class Solution:
5 def countDifferentSubsequenceGCDs(self, nums: List[int]) -> int:
6 # Find the maximum value in the list to set the range for checking
7 max_num = max(nums)
8
9 # Create a set from the list for faster membership testing
10 unique_nums = set(nums)
11
12 # Initialize the answer count
13 count = 0
14
15 # Iterate over all possible gcd values
16 for x in range(1, max_num + 1):
17 # Initialize greatest common divisor for the current number x
18 current_gcd = 0
19
20 # Iterate through multiples of x
21 for y in range(x, max_num + 1, x):
22 # If multiple y exists in the original list, calculate its gcd with the current gcd
23 if y in unique_nums:
24 current_gcd = gcd(current_gcd, y)
25 # If the gcd equals the current number x, then increment the count
26 if current_gcd == x:
27 count += 1
28 break
29
30 return count
31
1import java.util.Arrays;
2
3class Solution {
4 // Method to count the number of different subsequence GCDs in the given array.
5 public int countDifferentSubsequenceGCDs(int[] nums) {
6 // Find the maximum value in the array to define the range of possible GCDs
7 int maxVal = Arrays.stream(nums).max().getAsInt();
8
9 // Initialize an array to keep track of visited numbers within the range
10 boolean[] visited = new boolean[maxVal + 1];
11
12 // Mark numbers that are present in the input array
13 for (int num : nums) {
14 visited[num] = true;
15 }
16
17 // Counter for the number of distinct subsequence GCDs
18 int count = 0;
19
20 // Iterate through all possible values to check if they can be a GCD of a subsequence
21 for (int candidate = 1; candidate <= maxVal; ++candidate) {
22 int gcdValue = 0;
23 // Check multiples of the candidate if they are visited and calculate the GCD
24 for (int multiple = candidate; multiple <= maxVal; multiple += candidate) {
25 if (visited[multiple]) {
26 gcdValue = gcd(gcdValue, multiple);
27 // If the GCD equals the candidate, increment count and exit loop
28 if (candidate == gcdValue) {
29 ++count;
30 break;
31 }
32 }
33 }
34 }
35
36 // Return the total count of different subsequence GCDs
37 return count;
38 }
39
40 // Helper method to calculate the GCD of two numbers using Euclidean algorithm
41 private int gcd(int a, int b) {
42 return b == 0 ? a : gcd(b, a % b);
43 }
44}
45
1class Solution {
2public:
3 // Function to count the number of different subsequences with distinct GCDs.
4 int countDifferentSubsequenceGCDs(vector<int>& nums) {
5 // Find the maximum element in the nums array.
6 int maxElement = *max_element(nums.begin(), nums.end());
7 // Initialize a boolean vector to mark visited elements in the range up to maxElement.
8 vector<bool> visited(maxElement + 1, false);
9 // Mark the existing elements in nums as visited.
10 for (int num : nums) {
11 visited[num] = true;
12 }
13 // Initialize a counter for the number of different GCDs.
14 int totalCount = 0;
15 // Iterate over each possible number x from 1 to maxElement.
16 for (int x = 1; x <= maxElement; ++x) {
17 // Initialize gcd to 0 for this iteration.
18 int gcd = 0;
19 // Check multiples of x within the range up to maxElement for GCD calculations.
20 for (int multiple = x; multiple <= maxElement; multiple += x) {
21 // If the current multiple has been visited, it is in the original nums array.
22 if (visited[multiple]) {
23 gcd = std::gcd(gcd, multiple); // Compute GCD of current gcd and the multiple.
24 // If GCD equals x at any point, increment totalCount and break out of the loop.
25 if (gcd == x) {
26 totalCount++;
27 break;
28 }
29 }
30 }
31 }
32 // Return the total count of distinct GCDs.
33 return totalCount;
34 }
35};
36
37// Note: The method 'std::gcd' is part of the numeric header and C++17 standard library.
38// Therefore, it is assumed that the appropriate header file is included:
39// #include <numeric>
40
1// Import necessary functions from math-related libraries.
2import { max, gcd } from 'mathjs';
3
4// Function to count the number of different subsequences with distinct GCDs.
5function countDifferentSubsequenceGCDs(nums: number[]): number {
6 // Find the maximum element in the nums array.
7 const maxElement = max(nums) as number;
8 // Initialize an array to mark whether an element in the range up to maxElement was visited.
9 const visited: boolean[] = new Array(maxElement + 1).fill(false);
10 // Mark the existing elements in nums as visited.
11 nums.forEach(num => {
12 visited[num] = true;
13 });
14 // Initialize a counter for the number of different GCDs.
15 let totalCount = 0;
16 // Iterate over each possible number x from 1 to maxElement.
17 for (let x = 1; x <= maxElement; ++x) {
18 // Initialize gcd to 0 for this iteration.
19 let currentGCD = 0;
20 // Check multiples of x within the range up to maxElement for GCD calculations.
21 for (let multiple = x; multiple <= maxElement; multiple += x) {
22 // If the current multiple has been visited, it is in the original nums array.
23 if (visited[multiple]) {
24 // Compute GCD of current gcd and the multiple.
25 currentGCD = gcd(currentGCD, multiple) as number;
26 // If GCD equals x at any point, increment totalCount and break out of the loop.
27 if (currentGCD === x) {
28 totalCount++;
29 break;
30 }
31 }
32 }
33 }
34 // Return the total count of distinct GCDs.
35 return totalCount;
36}
37
38// For TypeScript, we assume that an appropriate math library like `mathjs` is being used,
39// and it should be imported at the beginning of the file.
40// Note: The function 'gcd' used here refers to the greatest common divisor function
41// which may be a part of a third-party mathematics library in TypeScript.
42
Time and Space Complexity
The code defines a function that counts the number of different greatest common divisors (GCDs) of all subsequences of the input array.
Time Complexity:
Let's denote n
as the length of the array nums
and mx
as the maximum value in nums
.
First, the function computes the maximum value from nums
using max(nums)
, which takes O(n)
time.
Next, the function iterates from 1
to mx
with nested loops:
- The outer loop runs
mx
times. - The inner loop runs at most
mx / x
times for each value ofx
, because it increments byx
on each iteration.
Within the inner loop, operations include checking membership in a set vis
and computing the GCD, both of which are O(1)
operations on average due to hashing for set membership and efficient algorithms for computing GCD (like Euclid's algorithm).
The costly part comes from the nested loops, so the total time complexity is:
O(n) + O(mx * ∑(1 .. mx) (1/x))
which simplifies to O(n) + O(mx * ln(mx))
because the harmonic series ∑(1 .. mx) (1/x)
roughly approximates the natural logarithm of mx
.
Thus, the time complexity of the function is O(n + mx * ln(mx))
.
Space Complexity:
The space is spent on:
- Storing
vis
, which is a set of elements innums
. It therefore requiresO(n)
space. - Storing the
mx
,g
, andans
variables, which requireO(1)
space.
Consequently, the space complexity of the function is O(n)
.
Learn more about how to find time and space complexity quickly using problem constraints.
Problem: Given a list of tasks and a list of requirements, compute a sequence of tasks that can be performed, such that we complete every task once while satisfying all the requirements.
Which of the following method should we use to solve this problem?
Recommended Readings
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
LeetCode Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way we
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https algomonster s3 us east 2 amazonaws com recursion jpg You first
Want a Structured Path to Master System Design Too? Don’t Miss This!