Taro Logo

X of a Kind in a Deck of Cards

Easy
Asked by:
Profile picture
Profile picture
33 views
Topics:
Arrays

You are given an integer array deck where deck[i] represents the number written on the ith card.

Partition the cards into one or more groups such that:

  • Each group has exactly x cards where x > 1, and
  • All the cards in one group have the same integer written on them.

Return true if such partition is possible, or false otherwise.

Example 1:

Input: deck = [1,2,3,4,4,3,2,1]
Output: true
Explanation: Possible partition [1,1],[2,2],[3,3],[4,4].

Example 2:

Input: deck = [1,1,1,2,2,2,3,3]
Output: false
Explanation: No possible partition.

Constraints:

  • 1 <= deck.length <= 104
  • 0 <= deck[i] < 104

Solution


Clarifying Questions

When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:

  1. What is the range of integer values within the `deck` array?
  2. Can `X` be zero or negative?
  3. If no `X` exists such that there are `X` of a kind, what value should I return (e.g., `false`, `0`, `null`)?
  4. Are all the cards represented as integers, or could they be other data types (e.g., strings)?
  5. If multiple values of `X` satisfy the condition, should I return the smallest, largest, or any valid `X` value?

Brute Force Solution

Approach

The brute force method involves checking all possible groupings of card values to see if a valid hand can be formed. We examine every possible size group to find one that satisfies the 'X of a Kind' condition. This approach guarantees a correct answer by exhaustively exploring all possibilities.

Here's how the algorithm would work step-by-step:

  1. First, count how many times each card value appears in the deck.
  2. Then, try to form groups of size 2 for each card value. Check if it is possible to divide the counts into groups of size 2.
  3. If not, try to form groups of size 3 for each card value. Again, check if it is possible to divide the counts into groups of size 3.
  4. Continue this process, increasing the group size (X) each time.
  5. At each step, if all card values can be divided into groups of size X, then we have found a solution.
  6. If we reach a group size where all card values are less than that group size, then there's no solution because there are not enough cards of the same value to make the group.

Code Implementation

def has_x_of_a_kind(deck):
    card_counts = {}
    for card in deck:
        card_counts[card] = card_counts.get(card, 0) + 1

    # Iterate through possible group sizes
    for group_size in range(2, len(deck) + 1):
        possible = True
        # Check if all card counts can be divided by group_size
        for card_value in card_counts:

            if card_counts[card_value] < group_size:
                return False

            if card_counts[card_value] % group_size != 0:
                # If any card count can't be divided, this group size is invalid
                possible = False
                break

        # If all card values can form groups of the current size, return True
        if possible:
            return True

    # If no valid group size was found, return False
    return False

Big(O) Analysis

Time Complexity
O(n*m)Let n be the number of cards in the input deck and m be the number of distinct card values. The first step involves counting the frequency of each card value, which takes O(n) time. The subsequent steps iterate through possible group sizes X, starting from 2. For each X, we iterate through the distinct card values (m) and check if each count is divisible by X. The largest possible X will be limited by n. Therefore, the maximum number of iterations needed to check all values is proportional to n*m.
Space Complexity
O(N)The algorithm first counts the occurrences of each card value. This requires a hash map (or an array if the card values are bounded) where the keys are the card values and the values are their counts. In the worst case, all N cards have different values, resulting in N key-value pairs being stored. Therefore, the auxiliary space used for counting is proportional to the number of cards N. Other variables used in the loops use constant space.

Optimal Solution

Approach

The key to solving this problem efficiently is to count how many of each card type there are, and then check if the greatest common divisor (GCD) of those counts is at least 2. This avoids needing to explore all possible combinations of card groupings.

Here's how the algorithm would work step-by-step:

  1. First, count how many times each card value appears in the deck. This will give you a list of counts for each unique card.
  2. Next, find the greatest common divisor (GCD) of all these counts. The GCD is the largest number that divides evenly into all of the counts.
  3. Finally, check if the GCD you found is greater than or equal to 2. If it is, then it's possible to divide the deck into groups of the same size, where each group has the same card value. If the GCD is less than 2, then it's not possible.

Code Implementation

def has_groups_size_x(deck_of_cards):
    card_counts = {}
    for card in deck_of_cards:
        card_counts[card] = card_counts.get(card, 0) + 1

    counts = list(card_counts.values())

    # Need GCD to determine the largest group size.
    def greatest_common_divisor(first_number, second_number):
        while(second_number):
            first_number, second_number = second_number, first_number % second_number
        return first_number

    group_size = counts[0]
    # Iterate through the counts to find the overall GCD
    for i in range(1, len(counts)):
        group_size = greatest_common_divisor(group_size, counts[i])

    # GCD must be >= 2 to form groups.
    if group_size >= 2:
        return True
    else:
        return False

Big(O) Analysis

Time Complexity
O(n + klogk + logm)The first step involves counting the occurrences of each card value, which takes O(n) time, where n is the number of cards in the deck. The number of unique card values is represented by k. Calculating the counts typically involves creating a hash map of size k where the insertion/increment is O(1). Next, we compute the greatest common divisor (GCD) of the counts. Computing the GCD of two numbers is typically O(log m) where m is the maximum of the two numbers. If there are k counts, we need to compute the GCD of k numbers which requires k-1 GCD operations and is thus O(klogm) in the worst case. Sorting or organizing the counts to facilitate efficient GCD calculation is O(klogk) (in the worst case). The total time complexity is then O(n + klogk + klogm). Since typically k <= n, the time complexity can be simplified to O(n + klogk + logm).
Space Complexity
O(1)The algorithm's auxiliary space usage is dominated by the space required to store the counts of each card value and to calculate the greatest common divisor (GCD). The number of unique card values is limited to the range of possible card values (e.g., 1 to 1000), making the size of the counts dictionary or array constant, independent of the input deck size N. Similarly, calculating the GCD involves a constant number of variables. Therefore, the auxiliary space complexity is O(1).

Edge Cases

Empty deck (cards array)
How to Handle:
Return false immediately because no groups of X can exist.
Deck with only one card
How to Handle:
Return false, because X must be at least 2.
Cards array contains a large number of cards with very few distinct values.
How to Handle:
The solution's space complexity depends on distinct card values, not total cards; handle large inputs efficiently.
All cards in the deck have the same value.
How to Handle:
Iterate through potential X values to find the largest that divides the card count.
Cards array contains negative numbers or zero.
How to Handle:
The problem constraints should specify the allowed range; if needed, filter out these values and proceed.
No value of X (where X >= 2) divides the count of all card values.
How to Handle:
Return false after checking all possible values of X up to the minimum card count.
Integer overflow when calculating counts for extremely large decks.
How to Handle:
Use a data type that can hold the maximum possible count, or add checks to prevent overflow.
The cards array contains a large number of distinct card values each occuring a prime number of times.
How to Handle:
The greatest common divisor (GCD) calculation can prematurely stop if one of the counts are 2.