Open In App

Smallest subarray with k distinct numbers

Last Updated : 28 Feb, 2025
Comments
Improve
Suggest changes
Like Article
Like
Report

We are given an array consisting of n integers and an integer k. We need to find the smallest subarray [l, r] (both l and r are inclusive) such that there are exactly k different numbers. If no such subarray exists, print -1 and If multiple subarrays meet the criteria, return the one with the smallest starting index.

Examples:

Input: arr[] = { 1, 1, 2, 2, 3, 3, 4, 5}
k = 3
Output: 5 7
Explanation: K Different numbers are present in range of [5,7] with the minimum range.


Input: arr[] = { 1, 2, 2, 3}
k = 2
Output: 0 1
Explanation: K Different numbers are present in range of [0,2] , with the minimum length and index.

Input: arr[] = {1, 1, 2, 1, 2}
k = 3
Output: Invalid k
Explanation: K Different Number is not present the array.

[Naive Approach] Generate all the Subarrays - O(n^2) Time and O(n) Space

We use nested for loops to generate all possible subarrays. For each subarray, we check if it contains exactly k distinct numbers. If it does, we compare its length with the current minimum and update l and r accordingly. If not find any subarray that contain k distinct numbers return -1.

C++
#include <bits/stdc++.h>
using namespace std;

// Prints the minimum range that contains exactly
// k distinct numbers.
void minRange(vector<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 << -1;

    else
        cout << start << " " << end;
}

// Driver code for above function.
int main()
{
    vector<int> arr = { 1, 2, 3, 4, 5 };
    int n = arr.size();
    int k = 3;
    minRange(arr, n, k);
    return 0;
}
Java
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(-1);

        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);
    }
}
Python
# 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(-1)
    else:
        print(l, r)

# Driver code 
if __name__ == "__main__":
    
    arr = [ 1, 2, 3, 4, 5 ]
    n = len(arr)
    k = 3
    minRange(arr, n, k)
C#
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(-1);
    }
    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);
}
}
JavaScript
// 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)
        console.log(-1);
    else
        console.log(l + " " + r);
}

// Driver code
let arr = [ 1, 2, 3, 4, 5 ];
let n = arr.length;
let k = 3;
minRange(arr, n, k);

Output
0 2

[Expected Approach] Sliding Window Approach - O(n) Time and O(k) Space

We can solve this problem efficiently using the Sliding Window technique with a map (hash table) to track the count of distinct numbers in the current window.

  • We expand the window by moving the right pointer (r) until we have at least k distinct elements.
  • If the number of distinct elements is greater than or equal to k, we attempt to shrink the window from the left (l) while maintaining the condition.
  • Whenever we find a valid window, we check if it is the smallest possible and update our result accordingly.
C++
#include <bits/stdc++.h>
using namespace std;

// prints the minimum range that contains exactly
// k distinct numbers.
void minRange(vector<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 << -1 << endl;

    else
        cout << start << " " << end << endl;
}

// Driver code for above function.
int main()
{
    vector<int> arr = { 1, 1, 2, 2, 3, 3, 4, 5 };
    int n = arr.size();
    int k = 3;
    minRange(arr, n, k);
    return 0;
}
Java
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(-1);
      
        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);
    }
}
Python
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(-1) 
    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) 
    
C#
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(-1);
  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);
}
}
JavaScript
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)
        console.log(-1);
    else
        console.log(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);

Output
5 7



Next Article

Similar Reads