3325. Count Substrings With K-Frequency Characters I
Problem Description
Given a string s
and an integer k
, return the total number of substrings of s
where at least one character appears at least k
times.
Intuition
To solve this problem, we use a sliding window technique to efficiently count the substrings meeting the condition. The idea is to maintain a window (a segment of the string) where we can count the characters using a frequency counter. We adjust the window’s left and right edges as we iterate through the string.
-
Sliding Window Approach: We iterate through the string using a pointer (
r
) for the right end. For each position, we add the character's count to a counter. -
Condition Check: If the character count in the window reaches
k
or more, it means that any substring starting from the beginning of the string to the left end (l-1
) and ending at the current position (r
) will meet the condition. -
Adjust Window: We then increment the left end (
l
) to decrease the count of the character at the left end until no character in the window has a count ofk
or more, ensuring that we only count substrings satisfying the problem requirements. -
Accumulate Result: For each valid window, add the number of valid starting points,
l
, to the answer.
By maintaining this window dynamically, we ensure that each examined substring is counted, resulting in an efficient calculation.
Learn more about Sliding Window patterns.
Solution Approach
The solution uses a Sliding Window approach to efficiently count the desired substrings in the given string s
. Here is the step-by-step process:
-
Initialize Variables:
cnt
: ACounter
from thecollections
module to track the frequency of each character within the current window.ans
: To store the total count of valid substrings. Initialized to 0.l
: The left end of the sliding window. Initialized to 0.
-
Iterate Over String:
- Use a loop to iterate over each character
c
in the strings
. For each character, perform the following:- Increase the count of
c
incnt
by 1 to reflect its presence in the current window.
- Increase the count of
- Use a loop to iterate over each character
-
Adjust Left End of Window:
- While the count of the current character
c
incnt
is greater than or equal tok
, it means any starting point for a substring from 0 tol-1
with the ending point at the current position satisfies the condition.- To remove such overlapping substrings, decrease the count of the character
s[l]
incnt
by 1 (effectively sliding the window to the right by moving its left edgel
). - Increment
l
to reflect the new position of the left edge.
- To remove such overlapping substrings, decrease the count of the character
- While the count of the current character
-
Count Valid Substrings:
- For each valid window (where no character frequency is
k
or more), addl
toans
, representing all valid substrings with the current character as the right endpoint.
- For each valid window (where no character frequency is
-
Return Result:
- After completing the iteration over the string,
ans
will hold the total count of substrings satisfying the given condition. Returnans
as the final result.
- After completing the iteration over the string,
This method efficiently manages the character counts and keeps adjusting the window's boundaries as required, allowing for optimal calculation of valid substrings within the given constraints.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's use the string s = "aabab"
and k = 2
to illustrate the sliding window approach for counting valid substrings where at least one character appears at least k
times.
-
Initialize Variables:
- Start with an empty dictionary
cnt
to keep track of character frequencies. - Set
ans = 0
to store the count of valid substrings. - Initialize
l = 0
to mark the left edge of the sliding window.
- Start with an empty dictionary
-
Iterate Over String:
-
Begin iterating with
r = 0
(wherer
is the right edge of the window):- Add
s[0] = 'a'
to thecnt
dictionary:{'a': 1}
. - Since no character appears
k
times, continue to the next character.
- Add
-
Move to
r = 1
:- Add
s[1] = 'a'
tocnt
:{'a': 2}
. - Now, 'a' appears at least
k
times (2 times). - We can count substrings ending at
r = 1
starting from any point0
tol
, so addl + 1 = 1
toans
.
- Add
-
-
Adjust Left End of Window:
-
Move
r = 2
:- Add
s[2] = 'b'
tocnt
:{'a': 2, 'b': 1}
. - 'a' still appears
k
times, so count the same substrings:ans += l + 1 = 2
.
- Add
-
Progress to
r = 3
:- Add
s[3] = 'a'
tocnt
:{'a': 3, 'b': 1}
. - Again, 'a' appears 3 times, which is more than
k
. Hence, count substrings:ans += l + 1 = 3
.
- Add
-
-
Continue Adjusting:
- Move to
r = 4
:- Add
s[4] = 'b'
tocnt
:{'a': 3, 'b': 2}
. - Both 'a' and 'b' appear at least
k
times. Now we adjust the window by incrementingl
and changingcnt
:- First, decrease
cnt['a']
by removings[l] = 'a'
:{'a': 2, 'b': 2}
, then incrementl = 1
. - After adjustment, no extra valid occurences to count.
- First, decrease
- Add
- Move to
Finally, ans
accumulates to 8, which is the total number of substrings where at least one character appears at least k
times. Return ans
.
Solution Implementation
1from collections import Counter
2
3class Solution:
4 def numberOfSubstrings(self, s: str, k: int) -> int:
5 # Initialize counter for characters
6 char_count = Counter()
7 # This will store the number of substrings
8 total_substrings = 0
9 # Left pointer for the sliding window
10 left = 0
11
12 # Iterate over each character in the string
13 for char in s:
14 # Increment the count of the current character
15 char_count[char] += 1
16
17 # If the count of the current character is at least 'k',
18 # shrink the window from the left until condition is invalidated
19 while char_count[char] >= k:
20 char_count[s[left]] -= 1
21 left += 1
22
23 # Add the current valid window size to the answer
24 total_substrings += left
25
26 return total_substrings
27
1class Solution {
2
3 // Method to count the number of valid substrings in the string "s"
4 // such that a character appears at least 'k' times in each substring.
5 public int numberOfSubstrings(String s, int k) {
6 // Array to store the frequency of each character in the current window
7 int[] frequency = new int[26];
8 int result = 0, left = 0;
9
10 // Iterate through each character in the string with 'right' as the end of the window
11 for (int right = 0; right < s.length(); ++right) {
12 // Convert the character to an index (0 for 'a', 1 for 'b', etc.)
13 int currentCharIndex = s.charAt(right) - 'a';
14
15 // Increment the count of the current character
16 ++frequency[currentCharIndex];
17
18 // While the current character count is at least 'k', shrink the window from the left
19 while (frequency[currentCharIndex] >= k) {
20 // Decrease the count of the character at 'left' and move 'left' forward
21 --frequency[s.charAt(left) - 'a'];
22 left++;
23 }
24
25 // Accumulate the count of valid substrings ending at 'right'
26 result += left;
27 }
28
29 return result; // Return the total count of valid substrings
30 }
31}
32
1class Solution {
2public:
3 // Method to count the number of substrings where each character appears at least 'k' times
4 int numberOfSubstrings(string s, int k) {
5 int n = s.size(); // Size of the input string
6 int ans = 0; // To store the final count of substrings
7 int left = 0; // Left pointer for the sliding window
8 int count[26] = {}; // Array to track the frequency of each character (a-z)
9
10 // Iterate over each character in the string
11 for (char& c : s) {
12 ++count[c - 'a']; // Increment the count for the current character
13
14 // While the current character count meets or exceeds 'k'
15 while (count[c - 'a'] >= k) {
16 --count[s[left++] - 'a']; // Move the left pointer to reduce the window size
17 }
18
19 ans += left; // Add the number of valid starting positions
20 }
21
22 return ans; // Return the total count of valid substrings
23 }
24};
25
1function numberOfSubstrings(s: string, k: number): number {
2 // Initialize answer (ans) and left pointer (l) to 0.
3 let ans = 0;
4 let l = 0;
5
6 // Create an array to count occurrences of each character.
7 const cnt: number[] = Array(26).fill(0);
8
9 // Iterate over each character in the string.
10 for (const c of s) {
11 // Calculate the index in the cnt array for current character.
12 const x = c.charCodeAt(0) - 'a'.charCodeAt(0);
13
14 // Increment the count for this character.
15 cnt[x]++;
16
17 // While the count of current character is greater than or equal to k,
18 // move the left pointer (l) and decrease count at the left pointer.
19 while (cnt[x] >= k) {
20 cnt[s[l++].charCodeAt(0) - 'a'.charCodeAt(0)]--;
21 }
22
23 // Add the value of l to the answer.
24 ans += l;
25 }
26
27 return ans;
28}
29
Time and Space Complexity
The time complexity of the code is O(n)
, where n
is the length of the string s
. This is because each character in the string is processed exactly once by both the loop that iterates through s
and the inner while
loop, due to the window sliding mechanism.
The space complexity is O(|Σ|)
, where |Σ|
is the size of the character set. In this case, the character set is the lowercase English letters, so |Σ| = 26
. This space is used by the Counter
to store the frequencies of characters currently in the sliding window.
Learn more about how to find time and space complexity quickly.
Which data structure is used in a depth first search?
Recommended Readings
https algomonster s3 us east 2 amazonaws com cover_photos stack svg Sliding Window Maximum Monotonic Stack We have an array and a sliding window defined by a start index and an end index The sliding window moves from left of the array to right There are always k elements in
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https algomonster s3 us east 2 amazonaws com recursion jpg You first
Want a Structured Path to Master System Design Too? Don’t Miss This!