Open In App

Smallest subarray with k distinct numbers

We are given an array consisting of n integers and an integer k. We need to find the minimum range in array [l, r] (both l and r are inclusive) such that there are exactly k different numbers. If such subarray doesn’t exist print “Invalid k”.
Examples: 

Input : arr[] = { 1, 1, 2, 2, 3, 3, 4, 5} 
            k = 3
Output : 5 7

Input : arr[] = { 1, 2, 2, 3} 
            k = 2
Output : 0 1

Input : arr[] = {1, 1, 2, 1, 2}
            k = 3
Output : Invalid k

Approach 1 : (Brute Force Method)



The simplest approach in this problem is, try to generate all the subarrays and check for which subarray the size is k. But there are some points we need to take care.

Steps:



  1. Pick each of the elements from the given array as the starting element [ i-th element ] of our required subarray.
  2. In each iteration initialize an empty set to store the distinct elements of the subarray
    • Pick each remaining element [ i, i+1,..n – 1] from the array as the last element [ j-th element ].
    • Add the current element to the set.
    • If the set size equals k then update the results and break from the inner loop (already found k distinct elements increasing the size of the subarray has 2 possibilities either will get more distinct elements, or increase the subarray size with repeated elements which are not to be considered in the required results).
  3. If (j == n) or j = size of the array, i.e. we have not found any desired subarray starting from i-th index and going forward we will be having fewer elements to consider.
    ( For example : consider given array is 4 5 5 4 5  and k = 3, when start from 0th index we will not find any subarray of k size and j will reach end so that means we won’t get any element that can make a k = 3 size required subarray). 
    So, Break from the outer loop.
  4. Print the output if found, otherwise, print “Invalid k”.

Implementation:




// C++ program to find minimum range that
// contains exactly k distinct numbers.
#include <bits/stdc++.h>
using namespace std;
 
// Prints the minimum range that contains exactly
// k distinct numbers.
void minRange(int arr[], int n, int k)
{
    // Starting and ending  index of resultant subarray
    int start = 0, end = n;
 
    // Selecting each element as the start index for
    // subarray
    for (int i = 0; i < n; i++) {
        // Initialize a set to store all distinct elements
        unordered_set<int> set;
 
        // Selecting the end index for subarray
        int j;
        for (j = i; j < n; j++) {
            set.insert(arr[j]);
 
            /*
            If set contains exactly k elements,
            then check subarray[i, j] is smaller in size
            than the current resultant subarray
            */
            if (set.size() == k) {
                if (j - i < end - start) {
                    start = i;
                    end = j;
                }
 
                // There are already k distinct elements
                // now, no need to consider further elements
                break;
            }
        }
 
        // If there are no k distinct elements
        // left in the array starting from index i we will
        // break
        if (j == n) {
            break;
        }
    }
 
    // If no window found then print -1
    if (start == 0 && end == n)
        cout << "Invalid k";
 
    else
        cout << start << " " << end;
}
 
// Driver code for above function.
int main()
{
    int arr[] = { 1, 2, 3, 4, 5 };
    int n = sizeof(arr) / sizeof(arr[0]);
    int k = 3;
    minRange(arr, n, k);
    return 0;
}
 
// This code is contributed by Rajdeep




// Java program to find minimum
// range that contains exactly
// k distinct numbers.
import java.util.*;
import java.util.ArrayList;
import java.util.HashSet;
 
class GFG {
 
    // Prints the minimum range
    // that contains exactly k
    // distinct numbers.
    static void minRange(int arr[], int n, int k)
    {
        // start -> start index of resultant subarray
        // end   -> end index of resultant subarray
        int start = 0;
        int end = n;
 
        // Selecting each element as the start index for
        // subarray
        for (int i = 0; i < n; i++) {
            // Initialize a set to store all distinct
            // elements
            HashSet<Integer> set = new HashSet<Integer>();
 
            // Selecting the end index for subarray
            int j;
            for (j = i; j < n; j++) {
                set.add(arr[j]);
 
                /*
                      If set contains exactly k
                    elements,then check subarray[i, j]
                    is smaller in size than the current
                    resultant subarray
                */
               
                if (set.size() == k)
                {
                    if (j - i < end - start) {
                        start = i;
                        end = j;
                    }
 
                    // There are already 'k' distinct
                    // elements now, no need to consider
                    // further elements
                    break;
                }
            }
 
            // If there are no k distinct elements left
              // in the array starting from index i we will break
            if (j == n)
                break;
        }
         
          // If no window found then print -1
        if (start == 0 && end == n)
            System.out.println("Invalid k");
 
        else
            System.out.println(start + " " + end);
    }
 
    // Driver code
    public static void main(String args[])
    {
        int arr[] = { 1, 2, 3, 4, 5 };
        int n = arr.length;
        int k = 3;
        minRange(arr, n, k);
    }
}
 
// This code is contributed by Rajdeep




# Python 3 program to find minimum range
# that contains exactly k distinct numbers.
 
# Prints the minimum range that contains
# exactly k distinct numbers.
def minRange(arr, n, k):
 
    l = 0
    r = n
 
    # Consider every element as
    # starting point.
    for i in range(n):
 
        # Find the smallest window starting
        # with arr[i] and containing exactly
        # k distinct elements.
        s = []
        for j in range(i, n) :
            s.append(arr[j])
            if (len(s) == k):
                if ((j - i) < (r - l)) :
                    r = j
                    l = i
                 
                break
 
        # There are less than k distinct
        # elements now, so no need to continue.
        if (j == n):
            break
 
    # If there was no window with k distinct
    # elements (k is greater than total
    # distinct elements)
    if (l == 0 and r == n):
        print("Invalid k")
    else:
        print(l, r)
 
# Driver code
if __name__ == "__main__":
     
    arr = [ 1, 2, 3, 4, 5 ]
    n = len(arr)
    k = 3
    minRange(arr, n, k)
 
# This code is contributed
# by ChitraNayal




// C#  program to find minimum 
// range that contains exactly 
// k distinct numbers.
using System;
using System.Collections.Generic;
 
public class GFG
{
 
// Prints the minimum range 
// that contains exactly k 
// distinct numbers.
public static void minRange(int[] arr, int n, int k)
{
    int l = 0, r = n;
 
    // Consider every element 
    // as starting point.
    for (int i = 0; i < n; i++)
    {
 
        // Find the smallest window 
        // starting with arr[i] and 
        // containing exactly k 
        // distinct elements.
        ISet<int> s = new HashSet<int>();
        int j;
        for (j = i; j < n; j++)
        {
            s.Add(arr[j]);
            if (s.Count == k)
            {
                if ((j - i) < (r - l))
                {
                    r = j;
                    l = i;
                }
                break;
            }
        }
 
        // There are less than k 
        // distinct elements now, 
        // so no need to continue.
        if (j == n)
        {
            break;
        }
    }
 
    // If there was no window 
    // with k distinct elements 
    // (k is greater than total 
    // distinct elements)
    if (l == 0 && r == n)
    {
        Console.WriteLine("Invalid k");
    }
    else
    {
        Console.WriteLine(l + " " + r);
    }
}
 
// Driver code 
public static void Main(string[] args)
{
    int[] arr = new int[] {1, 2, 3, 4, 5};
    int n = arr.Length;
    int k = 3;
    minRange(arr, n, k);
}
}
 
// This code is contributed by Shrikant13




<script>
// Javascript program to find minimum
// range that contains exactly
// k distinct numbers.
     
    // Prints the minimum range
    // that contains exactly k
    // distinct numbers.
    function minRange(arr,n,k)
    {
        let l = 0, r = n;
  
    // Consider every element
    // as starting point.
    for (let i = 0; i < n; i++)
    {
  
        // Find the smallest window
        // starting with arr[i] and
        // containing exactly k
        // distinct elements.
        let s = new Set();
        let j;
        for (j = i; j < n; j++)
        {
            s.add(arr[j]);
            if (s.size == k)
            {
                if ((j - i) < (r - l))
                {
                    r = j;
                    l = i;
                }
                break;
            }
        }
  
        // There are less than k
        // distinct elements now,
        // so no need to continue.
        if (j == n)
            break;
    }
  
    // If there was no window
    // with k distinct elements
    // (k is greater than total
    // distinct elements)
    if (l == 0 && r == n)
        document.write("Invalid k");
    else
        document.write(l + " " + r);
    }
     
    // Driver code
    let arr=[1, 2, 3, 4, 5];
    let n = arr.length;
    let k = 3;
    minRange(arr, n, k);
     
     
    // This code is contributed by avanitrachhadiya2155
</script>

Output
0 2

Time Complexity : O(N^2) ,where N is the number of elements in the array. Every time picking the end points of the subarray using two nested loops(one inside another) makes the time complexity O(N^2).
Space Complexity :  O(N), In the worst case, we can have all ‘N’ elements in our set.

Approach 2 : (Sliding Window Approach)

Optimization is get rid of the repeated work while making all subarray, all subarray will not help to find the resultant. The approach is –

Steps :

  • Initialize a map to store the frequencies of each element.
  • Taking two variables as taken before : start and end of the required subarray.
  • And here we are using i and j as the starting and ending index of the window respectively, initializing as i = 0 and j = 0.
  • Will traverse the array while the ending pointer of our window reach the end of given array. i.e.  while( j < n)
    1. Add the current element to the map map[ arr[j] ]++ and make j pointing to the next index
    2. Consider the window [ i, j-1 ] (reason for ‘j-1’ is as we incremented the value of ‘j’ just after insertion in last step) check whether its size is equal to k
    3. If window size is lesser than k then continue
    4. But if window size == k, then check its length whether it is the resultant subarray or not. 
    5. After that we need to move our window, but in order to move our window, we have to check the starting element of our current window (i.e. i-th). If the i-th element is having a frequency of 1 then erase it from the map and else decrease its frequency by 1. And increase the i-value. Make i to point to the next element.

( For understanding the reason of erase and decreasing frequency, take an example :  4 2 2 3 4 4 3 and k = 3  when we are dealing with the window 2 2 3 4 then ‘i’ would have pointed to the start of window (first 2) and ‘j’ would have pointed to the last of window (at 4). Now while moving forward (by one position), if the window totally erase 2 from the map, (and make window 2 3 4 4) then map would contain the information that 2 is not in the map but it is wrong so we will decrease the count of 2. Similarly, in case of having frequency == 1, and about to leave the window, the map should not contain the frequency of the element which not there in the window. )

Implementation:




// C++ program to find minimum range that
// contains exactly k distinct numbers.
#include <bits/stdc++.h>
using namespace std;
 
// prints the minimum range that contains exactly
// k distinct numbers.
void minRange(int arr[], int n, int k)
{
    /*
        start = starting index of resultant subarray
        end  = ending index of resultant subarray
    */
    int start = 0, end = n;
 
    unordered_map<int, int> map;
 
    /*
        i = starting index of the window (on left side)
        j = ending index of the window (on right side)
    */
    int i = 0, j = 0;
 
    while (j < n) {
        // Add the current element to the map
        map[arr[j]]++;
        j++;
 
        // Nothing to do when having less element
        if (map.size() < k)
            continue;
 
        /*
                If map contains exactly k elements,
                consider subarray[i, j - 1] keep removing
                left most elements
        */
 
        while (map.size() == k) {
            // as considering the (j-1)th and i-th index
            int windowLen = (j - 1) - i + 1;
            int subArrayLen = end - start + 1;
 
            if (subArrayLen > windowLen) {
                start = i;
                end = j - 1;
            }
 
            // Remove elements from left
 
            // If freq == 1 then totally erase
            if (map[arr[i]] == 1)
                map.erase(arr[i]);
 
            // decrease freq by 1
            else
                map[arr[i]]--;
 
            // move the starting index of window
            i++;
        }
    }
 
    if (start == 0 && end == n)
        cout << "Invalid k" << endl;
 
    else
        cout << start << " " << end << endl;
}
 
// Driver code for above function.
int main()
{
    int arr[] = { 1, 1, 2, 2, 3, 3, 4, 5 };
    int n = sizeof(arr) / sizeof(arr[0]);
    int k = 3;
    minRange(arr, n, k);
    return 0;
}
 
// This code is contributed by Rajdeep




// Java program to find minimum range that
// contains exactly k distinct numbers.
import java.util.*;
 
class GFG {
 
    // Prints the minimum range that contains exactly
    // k distinct numbers.
    static void minRange(int arr[], int n, int k)
    {
 
        /*
            start = starting index of resultant subarray
            end  = ending index of resultant subarray
        */
        int start = 0, end = n;
 
        HashMap<Integer, Integer> map = new HashMap<>();
 
        /*
            i = starting index of the window (on left side)
            j = ending index of the window (on right side)
        */
        int i = 0, j = 0;
       
        while (j < n) {
           
            // Add the current element to the map
            map.put(arr[j], map.getOrDefault(arr[j], 0) + 1);
            j++;
           
              // Nothing to do when having less element
            if (map.size() < k)
                continue;
 
            /*
                If map contains exactly k elements,
                consider subarray[i, j - 1] keep removing
                left most elements
                */
            while (map.size() == k)
            {
                  // as considering the (j-1)th and i-th index
                int windowLen = (j - 1) - i + 1;
                int subArrayLen = end - start + 1;
               
                if (windowLen < subArrayLen) {
                    start = i;
                    end = j - 1;
                }
 
                // Remove elements from left
               
                  // If freq == 1 then totally erase
                if (map.get(arr[i]) == 1)
                    map.remove(arr[i]);
                 
                  // decrease freq by 1
                else
                    map.put(arr[i], map.get(arr[i]) - 1);
                 
                  // move the starting index of window
                i++;
            }
        }
 
        if (start == 0 && end == n)
            System.out.println("Invalid k");
       
        else
            System.out.println(start + " " + end);
    }
 
    // Driver code
    public static void main(String[] args)
    {
        int arr[] = { 1, 1, 2, 2, 3, 3, 4, 5 };
        int n = arr.length;
        int k = 3;
 
        minRange(arr, n, k);
    }
}
 
// This code is contributed by Rajdeep




# Python3 program to find the minimum range
# that contains exactly k distinct numbers.
from collections import defaultdict
 
# Prints the minimum range that contains
# exactly k distinct numbers.
def minRange(arr, n, k):
  
    # Initially left and right side is -1
    # and -1, number of distinct elements
    # are zero and range is n.
    l, r = 0, n
    i = 0
    j = -1 # Initialize right side
     
    hm = defaultdict(lambda:0)
    while i < n:
      
        while j < n:
          
            # increment right side.
            j += 1
   
            # if number of distinct elements less than k.
            if len(hm) < k and j < n:
                hm[arr[j]] += 1
   
            # if distinct elements are equal to k
            # and length is less than previous length.
            if len(hm) == k and ((r - l) >= (j - i)):
              
                l, r = i, j
                break
   
        # if number of distinct elements less
        # than k, then break.
        if len(hm) < k:
            break
   
        # if distinct elements equals to k then
        # try to increment left side.
        while len(hm) == k:
   
            if hm[arr[i]] == 1:
                del(hm[arr[i]])
            else:
                hm[arr[i]] -= 1
   
            # increment left side.
            i += 1
   
            # it is same as explained in above loop.
            if len(hm) == k and (r - l) >= (j - i):
              
                l, r = i, j
          
        if hm[arr[i]] == 1:
            del(hm[arr[i]])
        else:
            hm[arr[i]] -= 1
             
        i += 1
   
    if l == 0 and r == n:
        print("Invalid k")
    else:
        print(l, r)
  
# Driver code for above function.
if __name__ == "__main__":
  
    arr = [1, 1, 2, 2, 3, 3, 4, 5
    n = len(arr)
    k = 3
    minRange(arr, n, k)
     
# This code is contributed by Rituraj Jain




// C# program to find minimum
// range that contains exactly
// k distinct numbers.
using System;
using System.Collections.Generic;
class GFG{
     
// Prints the minimum
// range that contains exactly
// k distinct numbers.
static void minRange(int []arr,
                     int n, int k)
{
  // Initially left and
  // right side is -1 and -1,
  // number of distinct
  // elements are zero and
  // range is n.
  int l = 0, r = n;
 
  // Initialize right side
  int j = -1;
 
  Dictionary<int,
             int> hm = new Dictionary<int,
                                      int>();
 
  for(int i = 0; i < n; i++)
  {
    while (j < n)
    {
      // Increment right side.
      j++;
 
      // If number of distinct elements less
      // than k.
      if (j < n && hm.Count < k)
        if(hm.ContainsKey(arr[j]))
          hm[arr[j]] = hm[arr[j]] + 1;
      else
        hm.Add(arr[j], 1);
 
      // If distinct elements are equal to k
      // and length is less than previous length.
      if (hm.Count == k &&
         ((r - l) >= (j - i)))
      {
        l = i;
        r = j;
        break;
      }
    }
 
    // If number of distinct elements less
    // than k, then break.
    if (hm.Count < k)
      break;
 
    // If distinct elements equals to k then
    // try to increment left side.
    while (hm.Count == k)
    {
      if (hm.ContainsKey(arr[i]) && 
          hm[arr[i]] == 1)
        hm.Remove(arr[i]);
      else
      {
        if(hm.ContainsKey(arr[i]))
          hm[arr[i]] = hm[arr[i]] - 1;
      }
 
      // Increment left side.
      i++;
 
      // It is same as explained in above loop.
      if (hm.Count == k &&
         (r - l) >= (j - i))
      {
        l = i;
        r = j;
      }
    }
    if (hm.ContainsKey(arr[i]) && 
        hm[arr[i]] == 1)
      hm.Remove(arr[i]);
    else
      if(hm.ContainsKey(arr[i]))
        hm[arr[i]] = hm[arr[i]] - 1;
  }
 
  if (l == 0 && r == n)
    Console.WriteLine("Invalid k");
  else
    Console.WriteLine(l + " " + r);
}
 
// Driver code
public static void Main(String[] args)
{
  int []arr = {1, 1, 2, 2,
               3, 3, 4, 5};
  int n = arr.Length;
  int k = 3;
  minRange(arr, n, k);
}
}
 
// This code is contributed by shikhasingrajput




<script>
// Javascript program to find minimum
// range that contains exactly
// k distinct numbers
     
     
    // Prints the minimum
// range that contains exactly
// k distinct numbers.
    function minRange(arr,n,k)
    {
        // Initially left and right side is -1 and -1,
    // number of distinct elements are zero and
    // range is n.
    let l = 0, r = n;
      
    // Initialize right side
    let j = -1;
      
    let hm = new Map();
      
    for(let i = 0; i < n; i++)
    {
        while (j < n)
        {
              
            // Increment right side.
            j++;
  
            // If number of distinct elements less
            // than k.
            if (j < n && hm.size < k)
            {
                if(hm.has(arr[j]))
                    hm.set(arr[j],
                       hm.get(arr[j]) + 1);
                else
                    hm.set(arr[j],1);
             
             }
            // If distinct elements are equal to k
            // and length is less than previous length.
            if (hm.size == k &&
                 ((r - l) >= (j - i)))
            {
                l = i;
                r = j;
                break;
            }
        }
  
        // If number of distinct elements less
        // than k, then break.
        if (hm.size < k)
            break;
  
        // If distinct elements equals to k then
        // try to increment left side.
        while (hm.size == k)
        {
            if (hm.has(arr[i]) && hm.get(arr[i]) == 1)
                hm.delete(arr[i]);
            else
                if(hm.has(arr[i]))
                    hm.set(arr[i],hm.get(arr[i]) - 1);
  
            // Increment left side.
            i++;
  
            // It is same as explained in above loop.
            if (hm.size == k &&
                  (r - l) >= (j - i))
            {
                l = i;
                r = j;
            }
        }
        if (hm.has(arr[i]) && hm.get(arr[i]) == 1)
            hm.delete(arr[i]);
        else
            if(hm.has(arr[i]))
                hm.set(arr[i],hm.get(arr[i]) - 1);
    }
  
    if (l == 0 && r == n)
        document.write("Invalid k");
    else
        document.write(l + " " + r);
    }
     
     
    // Driver code
    let arr=[1, 1, 2, 2, 3, 3, 4, 5];
    let n = arr.length;
    let k = 3;
    minRange(arr, n, k);
     
    // This code is contributed by rag2127
</script>

Output
5 7

Time Complexity : O(N) ,where N is the number of elements in the array. In the worst case, each element will be added once and removed once from the map.
Space Complexity :  O(K), In the worst case, we can have only ‘K’ elements in our map.

 


Article Tags :