Open In App

Count number of substrings with exactly k distinct characters

Given a string of lowercase alphabets, count all possible substrings (not necessarily distinct) that has exactly k distinct characters. 

Examples: 

Input: abc, k = 2
Output: 2
Possible substrings are {"ab", "bc"}

Input: aba, k = 2
Output: 3
Possible substrings are {"ab", "ba", "aba"}

Input: aa, k = 1
Output: 3
Possible substrings are {"a", "a", "aa"}

Method 1 (Brute Force): If the length of string is n, then there can be n*(n+1)/2 possible substrings. A simple way is to generate all the substring and check each one whether it has exactly k unique characters or not. If we apply this brute force, it would take O(n*n) to generate all substrings and O(n) to do a check on each one. Thus overall it would go O(n*n*n) 

Method 2: The problem can be solved in O(n*n). Idea is to maintain a hash table while generating substring and checking the number of unique characters using that hash table. 
The implementation below assume that the input string contains only characters from ‘a’ to ‘z’. 

Implementation 

// C++ program to count number of substrings with
// exactly k distinct characters in a given string
#include<bits/stdc++.h>
using namespace std;

// Function to count number of substrings
// with exactly k unique characters
int countkDist(string str, int k)
{
    int n = str.length();

    // Initialize result
    int res = 0;

    // To store count of characters from 'a' to 'z'
    int cnt[26];

    // Consider all substrings beginning with
    // str[i]
    for (int i = 0; i < n; i++)
    {
        int dist_count = 0;

        // Initializing array with 0
        memset(cnt, 0, sizeof(cnt));

        // Consider all substrings between str[i..j]
        for (int j=i; j<n; j++)
        {
            // If this is a new character for this
            // substring, increment dist_count.
            if (cnt[str[j] - 'a'] == 0)
                dist_count++;

            // Increment count of current character
            cnt[str[j] - 'a']++;

            // If distinct character count becomes k,
            // then increment result.
            if (dist_count == k)
                res++;
            if(dist_count > k) break;
        }
    }

    return res;
}

// Driver Program
int main()
{
    string str = "abcbaa";
    int k = 3;
    cout << "Total substrings with exactly "
         << k <<" distinct characters :"
         << countkDist(str, k) << endl;
    return 0;
}
// Java program to CountKSubStr number of substrings
// with exactly distinct characters in a given string
import java.util.Arrays;

public class CountKSubStr
{
    // Function to count number of substrings
    // with exactly k unique characters
    int countkDist(String str, int k)
    {
        // Initialize result
        int res = 0;

        int n = str.length();

        // To store seen characters from 'a' to 'z'
        boolean seen[] = new boolean[26];

        // Consider all substrings beginning with
        // str[i]
        for (int i = 0; i < n; i++)
        {
            int distCount = 0;

            // mark all chars as unseen
            Arrays.fill(seen, false);

            // Consider all substrings between str[i..j]
            for (int j=i; j<n; j++)
            {
                // If this is a new character for this
                // substring, increment dist_count.
                if (!seen[str.charAt(j) - 'a'])
                    distCount++;

                // mark current char as seen
                seen[str.charAt(j) - 'a'] = true;

                // If distinct character count becomes k,
                // then increment result.
                if (distCount == k)
                    res++;
            }
        }

        return res;
    }

    // Driver Program
    public static void main(String[] args)
    {
        CountKSubStr ob = new CountKSubStr();
        String ch = "abcbaa";
        int k = 3;
        System.out.println("Total substrings with exactly " +
                           k +    " distinct characters : "
                           + ob.countkDist(ch, k));
    }
}
// C# program to CountKSubStr number of substrings
// with exactly distinct characters in a given string

 
using System;
public class CountKSubStr
{
    // Function to count number of substrings
    // with exactly k unique characters
    int countkDist(string str, int k)
    {
        // Initialize result
        int res = 0;
 
        int n = str.Length;
 
        // To store count of characters from 'a' to 'z'
        int[] cnt = new int[26];
 
        // Consider all substrings beginning with
        // str[i]
        for (int i = 0; i < n; i++)
        {
            int dist_count = 0;
 
            // Initializing count array with 0
            Array.Clear(cnt, 0,cnt.Length);
 
            // Consider all substrings between str[i..j]
            for (int j=i; j<n; j++)
            {
                // If this is a new character for this
                // substring, increment dist_count.
                if (cnt[str[j] - 'a'] == 0)
                    dist_count++;
 
                // Increment count of current character
                cnt[str[j] - 'a']++;
 
                // If distinct character count becomes k,
                // then increment result.
                if (dist_count == k)
                    res++;
            }
        }
 
        return res;
    }
 
    // Driver Program
    public static void Main()
    {
        CountKSubStr ob = new CountKSubStr();
        string ch = "abcbaa";
        int k = 3;
        Console.Write("Total substrings with exactly " +
                           k +    " distinct characters : "
                           + ob.countkDist(ch, k));
    }
}
<script>

// javascript program to CountKSubStr number of substrings
// with exactly distinct characters in a given string

// Function to count number of substrings
// with exactly k unique characters
function countkDist(str , k)
{
    // Initialize result
    var res = 0;

    var n = str.length;

    // To store count of characters from 'a' to 'z'
    var cnt = Array.from({length: 26}, (_, i) => 0);

    // Consider all substrings beginning with
    // str[i]
    for (i = 0; i < n; i++)
    {
        var dist_count = 0;

       // Consider all substrings between str[i..j]
        for (j=i; j<n; j++)
        {
            // If this is a new character for this
            // substring, increment dist_count.
            if (cnt[str.charAt(j).charCodeAt(0) - 'a'.charCodeAt(0)] == 0)
                dist_count++;

            // Increment count of current character
            cnt[str.charAt(j).charCodeAt(0) - 'a'.charCodeAt(0)]++;

            // If distinct character count becomes k,
            // then increment result.
            if (dist_count == k)
                res++;
        }
    }

    return res;
}

// Driver Program
var ch = "abcbaa";
var k = 3;
document.write("Total substrings with exactly " +
                   k +    " distinct characters : "
                   + countkDist(ch, k));
                   
// This code contributed by shikhasingrajput 

</script>
<?php
// PHP program to CountKSubStr number of substrings
// with exactly distinct characters in a given string

    // Function to count number of substrings
    // with exactly k unique characters
    function countkDist($str, $k)
    {
        // Initialize result
        $res = 0;

        $n = strlen($str);

        // To store count of characters from 'a' to 'z'
        $cnt = array();

        // Consider all substrings beginning with
        // str[i]
        for ($i = 0; $i < $n; $i++)
        {
            $dist_count = 0;

            // Initializing count array with 0
            $cnt = array_fill(0, 0, true);

            // Consider all substrings between str[i..j]
            for ($j = $i; $j < $n; $j++)
            {
                // If this is a new character for this
                // substring, increment dist_count.
                if ($cnt[ord($str[$j]) - ord('a')] == 0)
                    $dist_count++;

                // Increment count of current character
                $cnt[ord($str[$j]) - ord('a')]++;

                // If distinct character count becomes k,
                // then increment result.
                if ($dist_count == $k)
                    $res++;
            }
        }

        return $res;
    }

    // Driver code
    {
        $ch = "abcbaa";
        $k = 3;
        echo("Total substrings with exactly " .
                        $k . " distinct characters : "
                        . countkDist($ch, $k));
    }

// This code is contributed by Code_Mech
def countkDist(s, k):
    n = len(s)
    res = 0

    # Consider all substrings beginning with str[i]
    for i in range(n):
        dist_count = 0
        cnt = [0] * 26  # To store count of characters from 'a' to 'z'

        # Consider all substrings between str[i..j]
        for j in range(i, n):
            # If this is a new character for this substring, increment dist_count.
            if cnt[ord(s[j]) - ord('a')] == 0:
                dist_count += 1

            # Increment count of current character
            cnt[ord(s[j]) - ord('a')] += 1

            # If distinct character count becomes k, then increment result.
            if dist_count == k:
                res += 1
            if dist_count > k:
                break

    return res


# Driver Program
if __name__ == "__main__":
    s = "abcbaa"
    k = 3
    print("Total substrings with exactly", k,
          "distinct characters:", countkDist(s, k))

Output
Total substrings with exactly 3 distinct characters :8

Time Complexity: O(n*n)
Auxiliary Space: O(1), Only 26 size array is used, which can be considered constant space.

Efficient Approach: The idea is to count all the subarrays with at most K distinct characters and then subtract all the subarrays with atmost K - 1 characters. That leaves us with count of subarrays with exactly K distinct characters.

Below is the implementation of the above approach:

#include <iostream>
#include <string>
#include <unordered_map>
using namespace std;

// the number of subarrays with at most K distinct elements
int most_k_chars(string& s, int k)
{
    if (s.size() == 0) {
        return 0;
    }
    unordered_map<char, int> map;
    int num = 0, left = 0;

    for (int i = 0; i < s.size(); i++) {
        map[s[i]]++;
        while (map.size() > k) {
            map[s[left]]--;
            if (map[s[left]] == 0) {
                map.erase(s[left]);
            }
            left++;
        }
        num += i - left + 1;
    }
    return num;
}

int exact_k_chars(string& s, int k)
{
    return most_k_chars(s, k) - most_k_chars(s, k - 1);
}

// Driver Program
int main()
{
    string s1 = "pqpqs";
    int k = 2;
    cout << "Total substrings with exactly " << k
         << " distinct characters : "
         << exact_k_chars(s1, k) << endl;

    string s2 = "aabab";
    k = 2;
    cout << "Total substrings with exactly " << k
         << " distinct characters : "
         << exact_k_chars(s2, k) << endl;
}
import java.io.*;
import java.util.HashMap;
public class GFG
{
  
  // the number of subarrays with at most K distinct
  // elements
  static int most_k_chars(String s, int k)
  {
    if (s.length() == 0) {
      return 0;
    }
    HashMap<Character, Integer> map = new HashMap<>();
    int num = 0, left = 0;

    for (int i = 0; i < s.length(); i++) {
      map.put(s.charAt(i),
              map.getOrDefault(s.charAt(i), 0) + 1);
      while (map.size() > k) {
        map.put(s.charAt(left),
                map.getOrDefault(s.charAt(left), 0)
                - 1);
        if (map.get(s.charAt(left)) == 0) {
          map.remove(s.charAt(left));
        }
        left++;
      }
      num += i - left + 1;
    }
    return num;
  }
  static int exact_k_chars(String s, int k)
  {
    return most_k_chars(s, k) - most_k_chars(s, k - 1);
  }
  public static void main(String[] args)
  {
    String s1 = "pqpqs";
    int k = 2;
    System.out.println("Total substrings with exactly "
                       + k + " distinct characters : "
                       + exact_k_chars(s1, k));

    String s2 = "aabab";
    k = 2;
    System.out.println("Total substrings with exactly "
                       + k + " distinct characters : "
                       + exact_k_chars(s2, k));
  }
}

// This code is contributed by garg28harsh.
using System;
using System.Collections.Generic;
class GFG {

  // the number of subarrays with at most K distinct
  // elements
  static int most_k_chars(string s, int k)
  {
    if (s.Length == 0) {
      return 0;
    }
    Dictionary<char, int> map
      = new Dictionary<char, int>();
    int num = 0, left = 0;

    for (int i = 0; i < s.Length; i++) {
      if (map.ContainsKey(s[i])) {
        int val = map[s[i]];
        map.Remove(s[i]);
        map.Add(s[i], val + 1);
      }
      else
        map.Add(s[i], 1);
      while (map.Count > k) {
        if (map.ContainsKey(s[left])) {
          int val = map[s[left]];
          map.Remove(s[left]);
          map.Add(s[left], val - 1);
        }
        else
          map.Add(s[left], -1);
        if (map[s[left]] == 0) {
          map.Remove(s[left]);
        }
        left++;
      }
      num += i - left + 1;
    }
    return num;
  }
  static int exact_k_chars(String s, int k)
  {
    return most_k_chars(s, k) - most_k_chars(s, k - 1);
  }
  public static void Main(string []args)
  {
    string s1 = "pqpqs";
    int k = 2;
    Console.WriteLine("Total substrings with exactly "
                      + k + " distinct characters : "
                      + exact_k_chars(s1, k));

    string s2 = "aabab";
    k = 2;
    Console.WriteLine("Total substrings with exactly "
                      + k + " distinct characters : "
                      + exact_k_chars(s2, k));
  }
}

// This code is contributed by garg28harsh.
function most_k_chars(s, k) {
  if (!s) {
    return 0;
  }
  const char_count = {};
  let num = 0;
  let left = 0;

  for (let i = 0; i < s.length; i++) {
    char_count[s[i]] = (char_count[s[i]] || 0) + 1;
    while (Object.keys(char_count).length > k) {
      char_count[s[left]] -= 1;
      if (char_count[s[left]] === 0) {
        delete char_count[s[left]];
      }
      left += 1;
    }
    num += i - left + 1;
  }
  return num;
}

function exact_k_chars(s, k) {
  return most_k_chars(s, k) - most_k_chars(s, k - 1);
}

// Driver Program
const s1 = "pqpqs";
let k = 2;
console.log(`Total substrings with exactly ${k} distinct characters: ${exact_k_chars(s1, k)}`);

const s2 = "aabab";
k = 2;
console.log(`Total substrings with exactly ${k} distinct characters: ${exact_k_chars(s2, k)}`);
# Python code

# the number of subarrays with at most K distinct elements
def most_k_chars(s, k):
    if not s:
        return 0
    char_count = {}
    num = 0
    left = 0

    for i in range(len(s)):
        char_count[s[i]] = char_count.get(s[i], 0) + 1
        while len(char_count) > k:
            char_count[s[left]] -= 1
            if char_count[s[left]] == 0:
                char_count.pop(s[left])
            left += 1
        num += i - left + 1
    return num

def exact_k_chars(s, k):
    return most_k_chars(s, k) - most_k_chars(s, k - 1)

# Driver Program
s1 = "pqpqs"
k = 2
print(f"Total substrings with exactly {k} distinct characters: {exact_k_chars(s1, k)}")

s2 = "aabab"
k = 2
print(f"Total substrings with exactly {k} distinct characters: {exact_k_chars(s2, k)}")

# This code is contributed by Aman Kumar

Output
Total substrings with exactly 2 distinct characters : 7
Total substrings with exactly 2 distinct characters : 9

Time Complexity: O(n)
Auxiliary Space: O(1)

Efficient Approach Without Map : The idea is to count all the subarrays with at most K distinct characters and then subtract all the subarrays with atmost K - 1 characters. That leaves us with count of subarrays with exactly K distinct characters. But in this Approach we are not using map instead vector of 26 size.

Below is the implementation of the above approach:

#include <iostream>
#include <vector>
using namespace std;

// Function to count the number of substrings with at most k distinct characters
int countSubstringsWithAtMostKDistinctChars(string &s, int k) {
    int left = 0, right = 0, n = s.size(), distinctCount = 0, substringCount = 0;

    // Vector to store the frequency of characters in the current substring
    vector<int> charFrequency(26, 0);

    while (right < n) {
        int charIndex = s[right] - 'a';
        charFrequency[charIndex]++;

        // If the frequency becomes 1, it means a new distinct character is added
        if (charFrequency[charIndex] == 1) {
            distinctCount++;
        }

        // While the number of distinct characters exceeds k, move the left pointer
        while (distinctCount > k) {
            charFrequency[s[left] - 'a']--;

            // If the frequency becomes 0, it means a distinct character is removed
            if (charFrequency[s[left] - 'a'] == 0) {
                distinctCount--;
            }

            left++;
        }

        // Add the count of substrings with at most k distinct characters
        substringCount += (right - left + 1);

        // Move the right pointer to expand the window
        right++;
    }

    return substringCount;
}

// Function to count the number of substrings with exactly k distinct characters
int countSubstringsWithExactlyKDistinctChars(string str, int k) {
    // Count substrings with at most k distinct characters
    int countAtMostK = countSubstringsWithAtMostKDistinctChars(str, k);

    // Count substrings with at most (k-1) distinct characters
    int countAtMostKMinus1 = countSubstringsWithAtMostKDistinctChars(str, k - 1);

    // The difference gives the count of substrings with exactly k distinct characters
    return countAtMostK - countAtMostKMinus1;
}

int main() {
    // Example usage
    string inputString = "aacfssa";
    int k = 3;
    int result = countSubstringsWithExactlyKDistinctChars(inputString, k);
    
    cout << "The number of substrings with exactly " << k << " distinct characters is: " << result << endl;

    return 0;
}
import java.util.Arrays;

public class Main {
    // Function to count the number of substrings with at most k distinct characters
    static int countSubstringsWithAtMostKDistinctChars(String s, int k) {
        int left = 0, right = 0, n = s.length(), distinctCount = 0, substringCount = 0;

        // Array to store the frequency of characters in the current substring
        int[] charFrequency = new int[26];

        while (right < n) {
            int charIndex = s.charAt(right) - 'a';
            charFrequency[charIndex]++;

            // If the frequency becomes 1, it means a new distinct character is added
            if (charFrequency[charIndex] == 1) {
                distinctCount++;
            }

            // While the number of distinct characters exceeds k, move the left pointer
            while (distinctCount > k) {
                charFrequency[s.charAt(left) - 'a']--;

                // If the frequency becomes 0, it means a distinct character is removed
                if (charFrequency[s.charAt(left) - 'a'] == 0) {
                    distinctCount--;
                }

                left++;
            }

            // Add the count of substrings with at most k distinct characters
            substringCount += (right - left + 1);

            // Move the right pointer to expand the window
            right++;
        }

        return substringCount;
    }

    // Function to count the number of substrings with exactly k distinct characters
    static int countSubstringsWithExactlyKDistinctChars(String str, int k) {
        // Count substrings with at most k distinct characters
        int countAtMostK = countSubstringsWithAtMostKDistinctChars(str, k);

        // Count substrings with at most (k-1) distinct characters
        int countAtMostKMinus1 = countSubstringsWithAtMostKDistinctChars(str, k - 1);

        // The difference gives the count of substrings with exactly k distinct characters
        return countAtMostK - countAtMostKMinus1;
    }

    public static void main(String[] args) {
        // Example usage
        String inputString = "aacfssa";
        int k = 3;
        int result = countSubstringsWithExactlyKDistinctChars(inputString, k);

        System.out.println("The number of substrings with exactly " + k + " distinct characters is: " + result);
    }
}

// This code is contributed by rambabuguphka
using System;

class Program
{
    static int CountSubstringsWithAtMostKDistinctChars(string s, int k)
    {
        int left = 0, right = 0, n = s.Length, distinctCount = 0, substringCount = 0;

        // Array to store the frequency of characters in the current substring
        int[] charFrequency = new int[26];

        while (right < n)
        {
            int charIndex = s[right] - 'a';
            charFrequency[charIndex]++;

            // If the frequency becomes 1, a new distinct character is added
            if (charFrequency[charIndex] == 1)
            {
                distinctCount++;
            }

            // While the number of distinct characters exceeds k, move the left pointer
            while (distinctCount > k)
            {
                charFrequency[s[left] - 'a']--;

                // If the frequency becomes 0, a distinct character is removed
                if (charFrequency[s[left] - 'a'] == 0)
                {
                    distinctCount--;
                }

                left++;
            }

            // Add the count of substrings with at most k distinct characters
            substringCount += (right - left + 1);

            // Move the right pointer to expand the window
            right++;
        }

        return substringCount;
    }

    static int CountSubstringsWithExactlyKDistinctChars(string s, int k)
    {
        // Count substrings with at most k distinct characters
        int countAtMostK = CountSubstringsWithAtMostKDistinctChars(s, k);

        // Count substrings with at most (k-1) distinct characters
        int countAtMostKMinus1 = CountSubstringsWithAtMostKDistinctChars(s, k - 1);

        // The difference gives the count of substrings with exactly k distinct characters
        return countAtMostK - countAtMostKMinus1;
    }

    static void Main(string[] args)
    {
        // Example usage
        string inputString = "aacfssa";
        int k = 3;
        int result = CountSubstringsWithExactlyKDistinctChars(inputString, k);

        Console.WriteLine("The number of substrings with exactly " + k + " distinct characters is: " + result);
    }
}
def count_substrings_with_at_most_k_distinct_chars(s, k):
    left, right, n, distinct_count, substring_count = 0, 0, len(s), 0, 0

    # List to store the frequency of characters in the current substring
    char_frequency = [0] * 26

    while right < n:
        char_index = ord(s[right]) - ord('a')
        char_frequency[char_index] += 1

        # If the frequency becomes 1, it means a new distinct character is added
        if char_frequency[char_index] == 1:
            distinct_count += 1

        # While the number of distinct characters exceeds k, move the left pointer
        while distinct_count > k:
            char_frequency[ord(s[left]) - ord('a')] -= 1

            # If the frequency becomes 0, it means a distinct character is removed
            if char_frequency[ord(s[left]) - ord('a')] == 0:
                distinct_count -= 1

            left += 1

        # Add the count of substrings with at most k distinct characters
        substring_count += (right - left + 1)

        # Move the right pointer to expand the window
        right += 1

    return substring_count


def count_substrings_with_exactly_k_distinct_chars(s, k):
    # Count substrings with at most k distinct characters
    count_at_most_k = count_substrings_with_at_most_k_distinct_chars(s, k)

    # Count substrings with at most (k-1) distinct characters
    count_at_most_k_minus_1 = count_substrings_with_at_most_k_distinct_chars(
        s, k - 1)

    # The difference gives the count of substrings with exactly k distinct characters
    return count_at_most_k - count_at_most_k_minus_1


def main():
    # Example usage
    input_string = "aacfssa"
    k = 3
    result = count_substrings_with_exactly_k_distinct_chars(input_string, k)

    print("The number of substrings with exactly",
          k, "distinct characters is:", result)


if __name__ == "__main__":
    main()

Output
The number of substrings with exactly 3 distinct characters is: 5

Time Complexity: O(n)
Auxiliary Space: O(1)

Article Tags :