Open In App

Number of substrings with equal character frequencies and fixed distance

Last Updated : 23 Mar, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

Given a string S and an integer K, the task is to count the number of substrings which have each character exactly K times and maximum distance between two same characters <= K.

Examples:

Input: S= ” de”, K = 2
Output: 3
Explanation: “abab”, “ababcc” and “cc” are substrings having each character exactly 2 times and the maximum distance between two same characters <= 2.

Input: S = “xyyxx”, K = 2
Output: 4
Explanation: “xyyx”, “yy”, “yyxx” and “xx” are substrings having each character exactly 2 times and the maximum distance between two same characters <= 2.

Approach: To solve the problem, follow the below idea:

The problem can be solved by considering the substrings of all possible lengths. For a particular length, iterate through all the windows of that length. In every window, check if every character occurred exactly K times and the distance between two same characters <= K. If yes, then increment the answer by 1. After iterating over windows of all possible lengths, print the answer.

Step-by-step algorithm:

  • Iterate through all possible substring lengths from 1 to the maximum length. For each substring length, use a sliding window approach to check if any substring of that length satisfies the conditions of being special.
  • Initialize a window and a character frequency map. The window represents the current substring being considered. The character frequency map stores the frequency of each character within the window.
  • Slide the window and update the character frequency map. Iterate through the characters within the window and update their frequencies in the character frequency map.
  • Check if the current window satisfies the conditions:
    • All character frequencies are equal to k or 0 (using the hasEqualFrequency function).
    • The difference between adjacent characters is at most k (using the hasAdjacentDifferenceAtMostTwo function).
  • If both conditions are met, increment the count of special substrings and Move the window forward by one character.
  • If the window has not reached the end of the word, increment the end index of the window and update the frequency of the new character entering the window in the character frequency map.
  • After iterating through all substring lengths, the algorithm returns the total count of special substrings found in the word.

Below is the implementation of the above approach:

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

// Function to check if all character frequencies are equal
// to k or 0
bool hasEqualFrequency(unordered_map<char, int>& charFreq,
                    int k)
{
    for (auto& entry : charFreq) {
        if (entry.second != k && entry.second != 0) {
            return false;
        }
    }
    return true;
}

// Function to check if adjacent character difference is at
// most two
bool hasAdjacentDifferenceAtMostTwo(string& word, int start,
                                    int end, int k)
{
    for (int i = start + 1; i <= end; ++i) {
        if (abs(word[i] - word[i - 1]) > k) {
            return false;
        }
    }
    return true;
}

int countSubstrings(string word, int k)
{
    int count = 0;
    // Count unique characters in the word
    int uniqueChars
        = unordered_set<char>(word.begin(), word.end())
            .size();

    // Loop through substring lengths from 1 to the count of
    // unique characters
    for (int subLen = 1; subLen <= uniqueChars; ++subLen) {
        // Define the window size
        int windowSize = subLen * k;

        // Map to store character frequencies
        unordered_map<char, int> charFreq;
        int start = 0;
        int end = start + windowSize - 1;

        // Populate the character frequency map for the
        // initial window
        for (int i = start;
            i <= min(end,
                    static_cast<int>(word.length()) - 1);
            ++i) {
            charFreq[word[i]]++;
        }

        // Sliding window approach
        while (end < word.length()) {
            // Check conditions for equal frequency and
            // adjacent difference at most two
            if (hasEqualFrequency(charFreq, k)
                && hasAdjacentDifferenceAtMostTwo(
                    word, start, end, k)) {
                count++;
            }

            // Move the window by decrementing the start
            // character frequency
            charFreq[word[start]]--;
            start++;
            end++;

            // Update the end character frequency if the
            // window size is less than word length
            if (windowSize < word.length()) {
                charFreq[word[end]]++;
            }
        }
    }

    return count;
}

int main()
{

    string word = "ababccde";
    int k = 2;

    int result = countSubstrings(word, k);

    cout << result << endl;

    return 0;
}
Java
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

public class SubstringCount {

    // Function to check if all character frequencies are
    // equal to k or 0
    private static boolean
    hasEqualFrequency(Map<Character, Integer> charFreq,
                      int k)
    {
        for (Map.Entry<Character, Integer> entry :
             charFreq.entrySet()) {
            if (entry.getValue() != k
                && entry.getValue() != 0) {
                return false;
            }
        }
        return true;
    }

    // Function to check if adjacent character difference is
    // at most two
    private static boolean
    hasAdjacentDifferenceAtMostTwo(String word, int start,
                                   int end, int k)
    {
        for (int i = start + 1; i <= end; ++i) {
            if (Math.abs(word.charAt(i)
                         - word.charAt(i - 1))
                > k) {
                return false;
            }
        }
        return true;
    }

    private static int countSubstrings(String word, int k)
    {
        int count = 0;

        // Count unique characters in the word
        List<Character> uniqueCharsList
            = word.chars()
                  .mapToObj(c -> (char)c)
                  .collect(Collectors.toList());
        int uniqueChars
            = new HashSet<>(uniqueCharsList).size();

        // Loop through substring lengths from 1 to the
        // count of unique characters
        for (int subLen = 1; subLen <= uniqueChars;
             ++subLen) {
            // Define the window size
            int windowSize = subLen * k;

            // Map to store character frequencies
            Map<Character, Integer> charFreq
                = new HashMap<>();
            int start = 0;
            int end = start + windowSize - 1;

            // Populate the character frequency map for the
            // initial window
            for (int i = start;
                 i <= Math.min(end, word.length() - 1);
                 ++i) {
                charFreq.put(
                    word.charAt(i),
                    charFreq.getOrDefault(word.charAt(i), 0)
                        + 1);
            }

            // Sliding window approach
            while (end < word.length()) {
                // Check conditions for equal frequency and
                // adjacent difference at most two
                if (hasEqualFrequency(charFreq, k)
                    && hasAdjacentDifferenceAtMostTwo(
                        word, start, end, k)) {
                    count++;
                }

                // Move the window by decrementing the start
                // character frequency
                charFreq.put(
                    word.charAt(start),
                    charFreq.get(word.charAt(start)) - 1);
                start++;
                end++;

                // Update the end character frequency if the
                // window size is less than or equal to word
                // length
                if (end < word.length()) {
                    charFreq.put(word.charAt(end),
                                 charFreq.getOrDefault(
                                     word.charAt(end), 0)
                                     + 1);
                }
            }
        }

        return count;
    }

    public static void main(String[] args)
    {
        String word = "ababccde";
        int k = 2;

        int result = countSubstrings(word, k);

        System.out.println(result);
    }
}
// This code is contributed by akshitaguprzj3
C#
using System;
using System.Collections.Generic;

class Program
{
    // Function to check if all character frequencies are equal to k or 0
    static bool HasEqualFrequency(Dictionary<char, int> charFreq, int k)
    {
        foreach (var value in charFreq.Values)
        {
            if (value != k && value != 0)
            {
                return false;
            }
        }
        return true;
    }

    // Function to check if adjacent character difference is at most two
    static bool HasAdjacentDifferenceAtMostTwo(string word, int start, int end, int k)
    {
        for (int i = start + 1; i <= end; ++i)
        {
            if (Math.Abs(word[i] - word[i - 1]) > k)
            {
                return false;
            }
        }
        return true;
    }

    static int CountSubstrings(string word, int k)
    {
        int count = 0;

        // Count unique characters in the word
        int uniqueChars = new HashSet<char>(word).Count;

        // Loop through substring lengths from 1 to the count of unique characters
        for (int subLen = 1; subLen <= uniqueChars; ++subLen)
        {
            // Define the window size
            int windowSize = subLen * k;

            // Dictionary to store character frequencies
            Dictionary<char, int> charFreq = new Dictionary<char, int>();
            int start = 0;
            int end = start + windowSize - 1;

            // Populate the character frequency map for the initial window
            for (int i = start; i <= Math.Min(end, word.Length - 1); ++i)
            {
                charFreq[word[i]] = charFreq.ContainsKey(word[i]) ? charFreq[word[i]] + 1 : 1;
            }

            // Sliding window approach
            while (end < word.Length)
            {
                // Check conditions for equal frequency and adjacent difference at most two
                if (HasEqualFrequency(charFreq, k) && HasAdjacentDifferenceAtMostTwo(word, start, end, k))
                {
                    count++;
                }

                // Move the window by decrementing the start character frequency
                charFreq[word[start]]--;
                start++;
                end++;

                // Update the end character frequency if the window size is less than or equal to word length
                if (end < word.Length)
                {
                    charFreq[word[end]] = charFreq.ContainsKey(word[end]) ? charFreq[word[end]] + 1 : 1;
                }
            }
        }

        return count;
    }

    static void Main(string[] args)
    {
        string word = "ababccde";
        int k = 2;

        int result = CountSubstrings(word, k);

        Console.WriteLine(result);
    }
}
Javascript
// Function to check if all character frequencies are equal
// to k or 0
function hasEqualFrequency(charFreq, k) {
    for (const entry of Object.entries(charFreq)) {
        if (entry[1] !== k && entry[1] !== 0) {
            return false;
        }
    }
    return true;
}

// Function to check if adjacent character difference is at
// most two
function hasAdjacentDifferenceAtMostTwo(word, start, end, k) {
    for (let i = start + 1; i <= end; ++i) {
        if (Math.abs(word[i].charCodeAt(0) - word[i - 1].charCodeAt(0)) > k) {
            return false;
        }
    }
    return true;
}

function countSubstrings(word, k) {
    let count = 0;

    // Count unique characters in the word
    const uniqueChars = new Set(word).size;

    // Loop through substring lengths from 1 to the count of
    // unique characters
    for (let subLen = 1; subLen <= uniqueChars; ++subLen) {
        // Define the window size
        const windowSize = subLen * k;

        // Map to store character frequencies
        const charFreq = {};

        let start = 0;
        let end = start + windowSize - 1;

        // Populate the character frequency map for the
        // initial window
        for (let i = start; i <= Math.min(end, word.length - 1); ++i) {
            charFreq[word[i]] = (charFreq[word[i]] || 0) + 1;
        }

        // Sliding window approach
        while (end < word.length) {
            // Check conditions for equal frequency and
            // adjacent difference at most two
            if (hasEqualFrequency(charFreq, k) && hasAdjacentDifferenceAtMostTwo(word, start, end, k)) {
                count++;
            }

            // Move the window by decrementing the start
            // character frequency
            charFreq[word[start]]--;

            start++;
            end++;

            // Update the end character frequency if the
            // window size is less than word length
            if (windowSize < word.length) {
                charFreq[word[end]] = (charFreq[word[end]] || 0) + 1;
            }
        }
    }

    return count;
}

// Example
const word = "ababccde";
const k = 2;

const result = countSubstrings(word, k);

console.log(result);
Python3
def has_equal_frequency(char_freq, k):
    # Check if all character frequencies are equal to k or 0
    for value in char_freq.values():
        if value != k and value != 0:
            return False
    return True

def has_adjacent_difference_at_most_two(word, start, end, k):
    # Check if adjacent character difference is at most two
    for i in range(start + 1, end + 1):
        if abs(ord(word[i]) - ord(word[i - 1])) > k:
            return False
    return True

def count_substrings(word, k):
    count = 0

    # Count unique characters in the word
    unique_chars = len(set(word))

    # Loop through substring lengths from 1 to the count of unique characters
    for sub_len in range(1, unique_chars + 1):
        # Define the window size
        window_size = sub_len * k

        # Dictionary to store character frequencies
        char_freq = {}
        start = 0
        end = start + window_size - 1

        # Populate the character frequency map for the initial window
        for i in range(start, min(end, len(word) - 1) + 1):
            char_freq[word[i]] = char_freq.get(word[i], 0) + 1

        # Sliding window approach
        while end < len(word):
            # Check conditions for equal frequency and adjacent difference at most two
            if has_equal_frequency(char_freq, k) and has_adjacent_difference_at_most_two(word, start, end, k):
                count += 1

            # Move the window by decrementing the start character frequency
            char_freq[word[start]] -= 1
            start += 1
            end += 1

            # Update the end character frequency if the window size is less than or equal to word length
            if end < len(word):
                char_freq[word[end]] = char_freq.get(word[end], 0) + 1

    return count

def main():
    word = "ababccde"
    k = 2

    result = count_substrings(word, k)

    print(result)

if __name__ == "__main__":
    main()

Output
3







Time Complexity: O(N), where N is the length of the string S.
Auxiliary Space: O(1)



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads