LC 1296. Divide Array in Sets of K Consecutive Numbers

Sarthak Sehgal · March 26, 2020

Solution

Approach 1

We can employ a greedy approach where we start from the smallest number in the array, say minNum, and check whether minNum+1, minNum+2, …minNum+k-1 exist and then so on for next smallest number. For this, we can do the following:

  • Count number of different numbers to a map (map stores number->frequency)
  • Loop from the smallest number in the map
  • Everytime we meet a new number i, we decrement frequency of numbers from i to i+k-1. If any number does not exist, we return false.

Code:

bool isPossibleDivide(vector<int> A, int k) {
    map<int, int> c;
    for (int i : A) c[i]++;
    for (auto it : c)
        if (c[it.first] > 0)
            for (int i = k - 1; i >= 0; --i)
                if ((c[it.first + i] -= c[it.first]) < 0)
                    return false;
    return true;
}

But this approach can be expensive when k is very large as the time taken is O(MlogM + MK) (MlogM for the map), where M is the number of different numbers in the array.

Approach 2

The question can be solved using a more general approach where we won’t have to look forward K-1 elements for each new element encountered. We can use a queue to store which stores for each element the minimum frequency of next K-1 elements it expects to have (i.e., for each element the number of K size arrays it will form as the first element in that array). Suppore the array is [1,2,2,3,3,4] and K=3 then for 1 we store 1 in queue (as it is expected to form [1,2,3]), for 2 we also store 1 (it is expected to form [1,2,3] and [2,3,4] but only [2,3,4] starts with 2), for 3 we will store 0 (it is expected to form [1,2,3] and [2,3,4] but none starts with 3), for 4 we store 0 (it is expected to form [2,3,4] and it doesn’t start with 4).

  • Count number of different elements to a map as in previous approach
  • “opened” represent current open straight groups (eg: when we come at 3, we are expecting groups starting from 1 and 2 to be there so opened=2)
  • In a queue, we record the number of straight group the element opens (= frequency of that element - opened. In the above example, when we come to 3, frequency(3)=2 and opened=2 so this means 3 will be used in both groups so it will not start any group of its own so we push 0 to queue)

Note that when queue’s size reaches K, this means that the group of first element in queue has been completed. So we pop that element and update “opened”. Eg: when we reach 3 above, opened=2 initially and after pushing 3’s frequency-opened queue size = 3. Now, this means that we have to pop the first element (=1). This is because the group [1,2,3] has been completed. We update opened: opened = opened - queue.front().

bool isPossibleDivide(vector<int>& nums, int k) {
    map<int, int> c;
    for (int i : nums)
        c[i]++;
    
    queue<int> start;
    int last_checked = -1, opened = 0;
    for (auto it : c) {
        int i = it.first;
        if (opened > 0 && i > last_checked + 1 || opened > c[i]) return false;
        start.push(c[i] - opened);
        last_checked = i, opened = c[i];
        if (start.size() == k) {
            opened -= start.front();
            start.pop();
        }
    }
    return opened == 0;
}

Time complexity: O(MlogM)

Twitter, Facebook