Number of substrings with equal character frequencies and fixed distance
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:
#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;
}
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
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);
}
}
// 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);
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)
Contact Us