846. Hand of straights
1 Question
Alice has some number of cards and she wants to rearrange the cards into groups so that each group is of size groupSize, and consists of groupSize consecutive cards.
Given an integer array hand where hand[i] is the value written on the ith card and an integer groupSize, return true if she can rearrange the cards, or false otherwise.
Example 1:
Input: hand = [1,2,3,6,2,3,4,7,8], groupSize = 3
Output: true
Explanation: Alice's hand can be rearranged as [1,2,3],[2,3,4],[6,7,8]
Example 2:
Input: hand = [1,2,3,4,5], groupSize = 4
Output: false
Explanation: Alice's hand can not be rearranged into groups of 4.
Constraints:
1 <= hand.length <= 1040 <= hand[i] <= 1091 <= groupSize <= hand.length
2 Intuition
The goal is to divide the list of cards (hand) into groups of groupSize, where each group consists of consecutive numbers.
A natural real-world analogy is playing cards — when we try to group our cards into sequences, we often:
- Start from the
smallest card, - Try to build a group of
groupSizeconsecutive cards, like[x, x+1, x+2, ...]. So, the greedy strategy is:
- Count the frequency of each card using
collections.Counter. - Sort the keys (or use a min-heap for efficient access to the smallest card).
- For each smallest available card, attempt to form a group starting from that card.
- For each card in the group, decrement its count.
- If any required card is missing or exhausted, return
False.
If all cards can be grouped successfully, return True.
3 Approach
3.1 Understand the Problem by Simulation
Try simulating small examples manually:
hand = [1,2,3,6,2,3,4,7,8], groupSize = 3Try to group them:
- Group 1: [1,2,3]
- Group 2: [2,3,4]
- Group 3: [6,7,8]
We realize:
- We always start from the smallest number available
- We try to form a consecutive group from that number
- We must consume one of each in the sequence
[x, x+1, ..., x+groupSize-1]
3.2 Greedy Choice Property
Ask: “At each step, what is the best immediate move?”
Answer:
Always form a group starting from the smallest card available, because delaying it may block forming valid groups later.
That’s greedy - always take the smallest ungrouped card and try to complete a group from it.
3.3 Track Resources
We need:
- A count of how many times each card appears -> use
Counter - Efficient access to the smallest available card -> use
Min-heap
3.4 Prove it Locally
Try:
- Can I always remove
groupSizeconsecutive numbers? - If not, can I return early?
We try to decrement counts and remove from the heap only when count becomes 0 to keep it consistent.
3.5 Edge Case Check
Think:
- If one card has more copies than others, can it still be grouped?
- What if the total number of cards isn’t divisible
Handle these early:
if len(hand) % groupSize != 0: return False4 Visualization
5 Complexity
5.1 Time Complexity
Let:
n = len(hand)(total number of cards)k = number of unique cards in hand
Breakdown:
- Building the frequency map:
O(n) - Sorting keys or using a heap:
O(k log k) - For each unique card, in worst case, we might scan up to
groupSizeconsecutive numbers → total time in worst case:O(n log k + n × groupSize)But since the number of total operations across all groups is bounded by n (each card is used once), we simplify:
Total Time Complexity: O(n log k)
(If we use a heap, the log factor applies to the number of unique cards.)
5.2 Space Complexity
Counter: stores up to k keys → O(k)
Optionally a heap: O(k)
No recursion or extra structures
Total Space Complexity: O(k)
6 Code
from typing import List
from collections import Counter
import heapq
class Solution:
def isNStraightHand(self, hand: List[int], groupSize: int) -> bool:
if len(hand) % groupSize != 0:
return False
count = Counter(hand)
min_heap = list(count.keys())
heapq.heapify(min_heap)
while min_heap:
first = min_heap[0]
for i in range(groupSize):
current = first + i
if count[current] == 0:
return False
count[current] -= 1
if count[current] == 0:
if current != min_heap[0]:
return False
heapq.heappop(min_heap)
return True