Post

LC 846 - Hand of Straights

LC 846 - Hand of Straights

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:

1
2
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:

1
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 <= 104
  • 0 <= hand[i] <= 109
  • 1 <= groupSize <= hand.length

Note: This question is the same as 1296. Divide Array in Sets of K Consecutive Numbers

Question here and solution here

Solution

concept

We will use a hashmap to keep track the frequency count We will also use a heap to keep track the smallest element, this element is the start of the sequence in each group

As we forming the sequence, we will decrement the count hashmap. Whenever there is number’s count reach 0, we will have to pop from the heap.

This question’s design is such that if we ever pop a number that is not the minimum in the min heap, we should return False. This is because there are still smaller number left in the heap, which will be used for other group, but if you pop the (larger) element this will create a hole in the other sequence, which will make the algorithm fail.

Note that we do not need to delete the number from the count dict when it reach 0. This is because if it ever reach 0 somewhere in the algorithm, it will be forced to pop from the heap and we can check the sequence validity from there.

code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Solution:
    def isNStraightHand(self, hand: List[int], groupSize: int) -> bool:
        """
        Greedy algo with min heap

        time complexity: O(nlogn)
        space complexity: O(n)

        keep track of the freq count of each number -> to use to construct number block
        keep track of the smallest element in the heap -> to select the smallest 
        """
        if len(hand) % groupSize:
            return False
        
        # freq count for each num
        count = {}
        for num in hand:
            if num in count:
                count[num] += 1
            else:
                count[num] = 1

        min_heap = list(count.keys())
        heapq.heapify(min_heap)
        
        while min_heap:
            # the first element in each block of numbers
            first = min_heap[0]
            # for one block of number
            # pop smallest if the freq count is 0
            for i in range(first, first + groupSize):
                if i not in count:
                    return False
                count[i] -= 1
                # edge case where the element we removed is 0 but it is not the smallest element in the heap
                # this will lead to a hole in the next number block if pop this number
                if count[i] == 0:
                    if i != min_heap[0]:
                        return False
                    heapq.heappop(min_heap)
        return True

Complexity

time: $O(n \log n)$
space: $O(n)$

This post is licensed under CC BY 4.0 by the author.