846. Hand of Straights

Dare2Solve

Dare2Solve

846. Hand of Straights
SAMSUNG 49-Inch Odyssey G9
SAMSUNG 49-Inch Odyssey G9
Because earth is not flat

Intuition

  1. Divisibility Check: First, we check if it's possible to form groups of the specified size. If the number of cards isn't divisible evenly by the group size, it's impossible to rearrange the cards as required.

  2. Counting and Heap: We count the occurrences of each card and maintain a min-heap to ensure we start forming groups with the smallest available card.

  3. Group Formation: For each group, we iterate through cards starting from the smallest available. We check if there are enough consecutive cards to form a group. If any card is missing or runs out, it's impossible to rearrange the cards as required.

  4. Validation and Updates: We update card counts as we form groups. If a card runs out, we remove it from the heap. We also ensure that the heap maintains the correct order.

Approach

  1. Divisibility Check:

    • We start by checking if it's possible to form groups of the specified size. If the number of cards isn't divisible evenly by the group size, it's impossible to rearrange the cards as required.
    if (hand.length % groupSize !== 0) return false;
    
  2. Counting and Heap:

    • We count the occurrences of each card using a hashmap. We also maintain a min-heap to ensure we start forming groups with the smallest available card.
    // Count occurrences of each card
    const cardCount = new Map();
    hand.forEach((card) => {
      cardCount.set(card, (cardCount.get(card) || 0) + 1);
    });
    
    // Use a min-heap to maintain the order of available cards
    const minHeap = new MinHeap();
    cardCount.forEach((count, card) => {
      minHeap.insert({ card, count });
    });
    
  3. Group Formation:

    • For each group, we iterate through cards starting from the smallest available.
    • We check if there are enough consecutive cards to form a group. If any card is missing or runs out, it's impossible to rearrange the cards as required.
    while (minHeap.size() > 0) {
      const smallest = minHeap.extractMin();
      const currentCard = smallest.card;
      const count = smallest.count;
    
      for (let i = 1; i < groupSize; i++) {
        const nextCard = currentCard + i;
        if (!cardCount.has(nextCard) || cardCount.get(nextCard) < count) {
          // Unable to form a group
          return false;
        }
        cardCount.set(nextCard, cardCount.get(nextCard) - count);
      }
    }
    
  4. Validation and Updates:

    • As we form groups, we update card counts in the hashmap. If a card runs out, we remove it from the heap. We also ensure that the heap maintains the correct order.
    // Update card counts and remove cards with count 0 from the heap
    cardCount.forEach((count, card) => {
      if (count === 0) {
        cardCount.delete(card);
      } else {
        minHeap.insert({ card, count });
      }
    });
    
  5. Return Result:

    • If we successfully form all required groups, we return true; otherwise, we return false.
    return true;
    

Complexity

Code

C++

class Solution {
public:
    bool isNStraightHand(vector<int>& hand, int groupSize) {
        if (hand.size() % groupSize != 0) return false;

        sort(hand.begin(), hand.end());
        map<int, int> cardCount;

        for (int x : hand) {
            cardCount[x]++;
        }

        for (int num : hand) {
            if (cardCount[num] > 0) {
                for (int i = 0; i < groupSize; ++i) {
                    int cur = num + i;
                    if (cardCount[cur] == 0) return false;
                    cardCount[cur]--;
                }
            }
        }

        return true;
    }
};

Python

class Solution:
    def isNStraightHand(self, hand: List[int], groupSize: int) -> bool:
        if len(hand) % groupSize != 0:
            return False

        hand.sort()
        cardCount = defaultdict(int)

        for card in hand:
            cardCount[card] += 1

        for card in hand:
            if cardCount[card] > 0:
                for i in range(groupSize):
                    currentCard = card + i
                    if cardCount[currentCard] == 0:
                        return False
                    cardCount[currentCard] -= 1

        return True

Java

class Solution {
    public boolean isNStraightHand(int[] hand, int groupSize) {
        if (hand.length % groupSize != 0) return false;

        Arrays.sort(hand);
        Map<Integer, Integer> cardCount = new HashMap<>();

        for (int x : hand) {
            cardCount.put(x, cardCount.getOrDefault(x, 0) + 1);
        }

        for (int num : hand) {
            if (cardCount.get(num) > 0) {
                for (int i = 0; i < groupSize; ++i) {
                    int cur = num + i;
                    if (cardCount.getOrDefault(cur, 0) == 0) return false;
                    cardCount.put(cur, cardCount.get(cur) - 1);
                }
            }
        }

        return true;
    }
}

JavaScript

/**
 * @param {number[]} hand
 * @param {number} groupSize
 * @return {boolean}
 */
var isNStraightHand = function (hand, groupSize) {
  if (hand.length % groupSize !== 0) return false;

  hand.sort((a, b) => a - b);
  var map = new Map();
  hand.forEach((x) => map.set(x, (map.get(x) || 0) + 1));

  for (var num of hand) {
    if (map.get(num)) {
      for (let i = 0; i < groupSize; ++i) {
        var cur = num + i;
        if (!map.has(cur) || map.get(cur) === 0) return false;
        map.set(cur, map.get(cur) - 1);
      }
    }
  }

  return true;
};

TypeScript

function isNStraightHand(hand: number[], groupSize: number): boolean {
  if (hand.length % groupSize !== 0) return false;

  hand.sort((a, b) => a - b);
  const map = new Map<number, number>();

  hand.forEach((x) => map.set(x, (map.get(x) || 0) + 1));

  for (const num of hand) {
    if (map.get(num)) {
      for (let i = 0; i < groupSize; ++i) {
        const cur = num + i;
        if (!map.has(cur) || map.get(cur) === 0) return false;
        map.set(cur, (map.get(cur) || 0) - 1);
      }
    }
  }

  return true;
}

Go

func isNStraightHand(hand []int, groupSize int) bool {
	if len(hand)%groupSize != 0 {
		return false
	}

	sort.Ints(hand)
	cardCount := make(map[int]int)

	for _, card := range hand {
		cardCount[card]++
	}

	for _, card := range hand {
		if cardCount[card] > 0 {
			for i := 0; i < groupSize; i++ {
				currentCard := card + i
				if cardCount[currentCard] == 0 {
					return false
				}
				cardCount[currentCard]--
			}
		}
	}

	return true
}

C#

public class Solution {
    public bool IsNStraightHand(int[] hand, int groupSize) {
        if (hand.Length % groupSize != 0) return false;

        Array.Sort(hand);
        Dictionary<int, int> cardCount = new Dictionary<int, int>();

        foreach (int x in hand) {
            if (cardCount.ContainsKey(x)) {
                cardCount[x]++;
            } else {
                cardCount[x] = 1;
            }
        }

        foreach (int num in hand) {
            if (cardCount[num] > 0) {
                for (int i = 0; i < groupSize; ++i) {
                    int cur = num + i;
                    if (!cardCount.ContainsKey(cur) || cardCount[cur] == 0) {
                        return false;
                    }
                    cardCount[cur]--;
                }
            }
        }

        return true;
    }
}

Conclusion

In this problem, Alice aims to rearrange a number of cards into groups, each consisting of a specified number of consecutive cards. We devised an algorithm to determine whether this rearrangement is possible.

Our approach involves several key steps:

  1. Divisibility Check: We first ensure that it's possible to form groups of the specified size by checking if the number of cards is divisible evenly by the group size.

  2. Counting and Heap: We count the occurrences of each card using a hashmap and maintain a min-heap to ensure we start forming groups with the smallest available card.

  3. Group Formation: For each group, we iterate through the cards starting from the smallest available. We check if there are enough consecutive cards to form a group. If any card is missing or runs out, it's impossible to rearrange the cards as required.

  4. Validation and Updates: As we form groups, we update card counts in the hashmap. If a card runs out, we remove it from the heap. We also ensure that the heap maintains the correct order.

Our solution provides an efficient way to determine if Alice can rearrange the cards as required. It leverages data structures like hashmaps and heaps to optimize the process.

In conclusion, this problem showcases the importance of algorithmic thinking and efficient data structure usage in solving real-world challenges. Through careful analysis and implementation, we're able to devise a solution that effectively addresses Alice's task.