914. X of a Kind in a Deck of Cards
Problem Description
You have a deck of cards where each card has a number written on it. The array deck[i]
represents the number on the i
th card.
Your task is to determine if you can partition all the cards into groups that meet these two conditions:
- Each group must contain exactly
x
cards, wherex > 1
(groups must have at least 2 cards) - All cards within the same group must have the same number written on them
Return true
if such a partition is possible, otherwise return false
.
For example, if you have cards [1,2,3,4,4,3,2,1]
, you could partition them into groups of size 2: [1,1]
, [2,2]
, [3,3]
, [4,4]
, so the answer would be true
.
The solution works by counting how many times each number appears in the deck using a Counter. Then it finds the greatest common divisor (GCD) of all these counts. If the GCD is at least 2, it means we can divide all cards into groups of size equal to the GCD (or any divisor of the GCD that's greater than 1), satisfying both conditions.
Intuition
Let's think about what conditions must be met for a valid partition to exist. If we have a number that appears 6 times, we could potentially group these cards into groups of size 2 (three groups), size 3 (two groups), or size 6 (one group). Notice that 2, 3, and 6 are all divisors of 6.
Now consider if we have two different numbers - one appearing 6 times and another appearing 9 times. For both to be divided into groups of the same size x
, that size x
must divide both 6 and 9. The possible values are the common divisors of 6 and 9, which are 1 and 3. Since groups must have size greater than 1, only x = 3
works.
This pattern extends to any number of different card values. For all cards to be partitioned into groups of size x
:
x
must divide the count of every distinct number in the deckx
must be greater than 1
The set of all possible values for x
is exactly the set of common divisors of all the counts. The largest such common divisor is the GCD (greatest common divisor) of all counts. If the GCD is at least 2, then we can use the GCD itself (or any of its divisors greater than 1) as our group size.
Therefore, the problem reduces to finding the GCD of all card counts and checking if it's at least 2. This elegant mathematical insight transforms a seemingly complex partitioning problem into a simple GCD calculation.
Learn more about Math patterns.
Solution Approach
The implementation follows a straightforward approach based on our GCD insight:
-
Count the occurrences: We use Python's
Counter
from the collections module to count how many times each distinct number appears in the deck. This creates a dictionary-like object where keys are the card numbers and values are their frequencies.cnt = Counter(deck)
-
Extract frequency values: The
cnt.values()
gives us all the frequency counts. For example, ifdeck = [1,2,3,4,4,3,2,1]
, thencnt
would be{1: 2, 2: 2, 3: 2, 4: 2}
, andcnt.values()
would be[2, 2, 2, 2]
. -
Calculate GCD of all frequencies: We use Python's
reduce
function with thegcd
function from the math module to find the greatest common divisor of all frequency values. Thereduce
function appliesgcd
cumulatively:- First, it calculates
gcd(freq1, freq2)
- Then
gcd(result, freq3)
- And so on until all frequencies are processed
reduce(gcd, cnt.values())
- First, it calculates
-
Check if valid partition exists: Finally, we check if the resulting GCD is at least 2. If it is, we can partition the cards into groups of size equal to the GCD (or any of its divisors greater than 1).
return reduce(gcd, cnt.values()) >= 2
The time complexity is O(n + m * log(max_count))
where n
is the number of cards and m
is the number of distinct values, as we count all cards once and then compute GCD for all frequencies. The space complexity is O(m)
for storing the frequency counts.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a concrete example with deck = [1,1,1,2,2,2,2,2,2]
.
Step 1: Count the occurrences We count how many times each card number appears:
- Number 1 appears 3 times
- Number 2 appears 6 times
So our frequency counts are {1: 3, 2: 6}
.
Step 2: Extract frequency values
From our counter, we get the list of frequencies: [3, 6]
.
Step 3: Calculate GCD of all frequencies We need to find the GCD of 3 and 6:
- Factors of 3: 1, 3
- Factors of 6: 1, 2, 3, 6
- Common factors: 1, 3
- Greatest common divisor: 3
Step 4: Check if valid partition exists
Since GCD = 3 ≥ 2, we return true
.
Verification: We can indeed partition the cards into groups of size 3:
- Group 1:
[1, 1, 1]
(all three 1's) - Group 2:
[2, 2, 2]
(first three 2's) - Group 3:
[2, 2, 2]
(remaining three 2's)
Each group has exactly 3 cards, and all cards in each group have the same number. The partition is valid!
Note that we could also use any divisor of 3 that's greater than 1. Since 3 is prime, there are no other such divisors in this case, but if our GCD had been 6, we could have used group sizes of 2, 3, or 6.
Solution Implementation
1from collections import Counter
2from math import gcd
3from functools import reduce
4from typing import List
5
6class Solution:
7 def hasGroupsSizeX(self, deck: List[int]) -> bool:
8 # Count the frequency of each card value in the deck
9 card_counts = Counter(deck)
10
11 # Find the GCD of all frequency counts
12 # If the GCD is at least 2, we can partition the deck into groups of equal size
13 # where each group contains cards of the same value
14 common_divisor = reduce(gcd, card_counts.values())
15
16 # Return True if we can form groups of size >= 2
17 return common_divisor >= 2
18
1class Solution {
2 /**
3 * Determines if the deck can be partitioned into groups where:
4 * 1. Each group has the same size X (X >= 2)
5 * 2. Each group contains cards with the same value
6 *
7 * The solution works by finding the GCD of all card frequencies.
8 * If the GCD is >= 2, we can partition the deck into groups of that size.
9 *
10 * @param deck array of integers representing card values
11 * @return true if valid partitioning exists, false otherwise
12 */
13 public boolean hasGroupsSizeX(int[] deck) {
14 // Count the frequency of each card value
15 Map<Integer, Integer> frequencyMap = new HashMap<>();
16 for (int cardValue : deck) {
17 frequencyMap.merge(cardValue, 1, Integer::sum);
18 }
19
20 // Initialize GCD with the frequency of the first card value
21 int gcdResult = frequencyMap.get(deck[0]);
22
23 // Calculate GCD of all frequencies
24 for (int frequency : frequencyMap.values()) {
25 gcdResult = gcd(gcdResult, frequency);
26 }
27
28 // Groups must have size at least 2
29 return gcdResult >= 2;
30 }
31
32 /**
33 * Calculates the Greatest Common Divisor (GCD) using Euclidean algorithm
34 *
35 * @param a first number
36 * @param b second number
37 * @return GCD of a and b
38 */
39 private int gcd(int a, int b) {
40 return b == 0 ? a : gcd(b, a % b);
41 }
42}
43
1class Solution {
2public:
3 bool hasGroupsSizeX(vector<int>& deck) {
4 // Count the frequency of each card value in the deck
5 unordered_map<int, int> frequencyMap;
6 for (int cardValue : deck) {
7 ++frequencyMap[cardValue];
8 }
9
10 // Initialize GCD with the frequency of the first card value
11 // Note: Using deck[0] to get a card value that definitely exists
12 int commonGCD = frequencyMap[deck[0]];
13
14 // Find the GCD of all frequencies
15 // If all groups can have the same size X, then X must divide all frequencies
16 // The maximum possible X is the GCD of all frequencies
17 for (auto& [cardValue, frequency] : frequencyMap) {
18 commonGCD = gcd(commonGCD, frequency);
19 }
20
21 // Groups must have at least 2 cards each
22 // Return true if the GCD is at least 2
23 return commonGCD >= 2;
24 }
25};
26
1/**
2 * Determines if a deck of cards can be divided into groups where:
3 * - Each group has the same size X (X >= 2)
4 * - Each group contains cards of the same value
5 * @param deck - Array of integers representing card values
6 * @returns true if deck can be divided into valid groups, false otherwise
7 */
8function hasGroupsSizeX(deck: number[]): boolean {
9 // Count frequency of each card value in the deck
10 const cardFrequencyMap: Record<number, number> = {};
11
12 for (const cardValue of deck) {
13 cardFrequencyMap[cardValue] = (cardFrequencyMap[cardValue] || 0) + 1;
14 }
15
16 /**
17 * Calculates the greatest common divisor using Euclidean algorithm
18 * @param a - First number
19 * @param b - Second number
20 * @returns The GCD of a and b
21 */
22 const calculateGCD = (a: number, b: number): number => {
23 return b === 0 ? a : calculateGCD(b, a % b);
24 };
25
26 // Initialize GCD with the frequency of the first card value
27 let commonGroupSize = cardFrequencyMap[deck[0]];
28
29 // Find GCD of all card frequencies
30 // This gives us the largest possible group size that divides all frequencies
31 for (const [_, frequency] of Object.entries(cardFrequencyMap)) {
32 commonGroupSize = calculateGCD(commonGroupSize, frequency);
33 }
34
35 // Groups must have at least 2 cards each
36 return commonGroupSize >= 2;
37}
38
Time and Space Complexity
Time Complexity: O(n + k × log M)
where n
is the length of the array deck
, k
is the number of unique values in deck
, and M
is the maximum count value in the Counter.
- Creating the Counter takes
O(n)
time to iterate through all elements indeck
- The
reduce(gcd, cnt.values())
operation performsk-1
GCD computations wherek
is the number of unique values - Each GCD computation between two numbers takes
O(log M)
time using the Euclidean algorithm, whereM
is the maximum of the two numbers being compared (in this case, the maximum count value) - Total:
O(n + k × log M)
, which simplifies toO(n × log M)
in the worst case whenk
approachesn
Space Complexity: O(k)
where k
is the number of unique values in deck
- The Counter object stores at most
k
key-value pairs wherek ≤ n
- The
reduce
function usesO(1)
additional space for the accumulator - The recursive call stack for GCD computations uses
O(log M)
space - Total:
O(k + log M)
, which can be written asO(n + log M)
in the worst case when all elements are unique
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Misunderstanding the Group Size Requirement
Pitfall: A common mistake is thinking that we need to find a specific group size x
that works for all cards, rather than understanding that the GCD gives us the maximum possible group size, and any divisor of the GCD greater than 1 also works.
Example: If card frequencies are [6, 9, 12]
, the GCD is 3. Students might think we can only form groups of size 3, but actually groups of size 3 work because:
- 6 cards can form 2 groups of 3
- 9 cards can form 3 groups of 3
- 12 cards can form 4 groups of 3
Solution: Remember that if GCD = g and g ≥ 2, then any divisor d of g where d ≥ 2 is a valid group size. The algorithm correctly handles this by checking if GCD ≥ 2.
2. Edge Case: Single Card Type
Pitfall: Not handling the case where all cards have the same number correctly, especially when there's only one unique card value in the deck.
Example: deck = [1, 1, 1, 1]
should return true
(can form groups of 2 or 4).
Solution: The current implementation handles this correctly since reduce(gcd, [4])
returns 4, which is ≥ 2. However, be aware that reduce
with a single-element iterable returns that element itself.
3. Empty Deck or Single Card
Pitfall: The code will fail on edge cases like an empty deck or a deck with just one card.
Example:
deck = []
will causereduce
to fail with "reduce() of empty sequence with no initial value"deck = [1]
should returnfalse
but the frequency count would be 1, andreduce(gcd, [1])
returns 1, which correctly givesfalse
Solution: Add validation at the beginning:
def hasGroupsSizeX(self, deck: List[int]) -> bool:
if len(deck) < 2:
return False
card_counts = Counter(deck)
common_divisor = reduce(gcd, card_counts.values())
return common_divisor >= 2
4. Misunderstanding GCD Behavior with Multiple Values
Pitfall: Not understanding how reduce(gcd, values)
works when there are multiple frequency counts.
Example: For frequencies [4, 6, 8]
:
- First:
gcd(4, 6) = 2
- Then:
gcd(2, 8) = 2
- Result: 2
Some might incorrectly calculate gcd(gcd(4, 6), 8) = gcd(2, 8) = 2
differently or expect a different order of operations.
Solution: The reduce function applies operations left-to-right sequentially, and GCD is associative, so the order doesn't matter. The result will always be the same regardless of computation order.
How does merge sort divide the problem into subproblems?
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
Coding Interview 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
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 assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!