Smallest subarray with k distinct numbers
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.
Table of Content
[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 exactlyk
distinct numbers. If it does, we compare its length with the current minimum and updatel
andr
accordingly. If not find any subarray that contain k distinct numbers return -1.
#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;
}
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);
}
}
# 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)
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);
}
}
// 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 leastk
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.
#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;
}
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);
}
}
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)
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);
}
}
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
Related Problems:
- Count Distinct Elements In Every Window of Size K
- Find first negative integer in every k size window
- Maximum of all subarray of size K
- Maximum MEX of all subarray of size K
- Sum of minimum and maximum elements of all subarrays of given size K