3329. Count Substrings With K-Frequency Characters II 🔒
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 efficiently, we can use a sliding window technique. The idea is to dynamically adjust a window of characters within the string s
while keeping track of character frequencies.
-
Sliding Window Setup: We maintain two pointers,
l
andr
, wherel
is the left boundary andr
is the right boundary of our sliding window. We traverse the string withr
moving forward, adding the character at positionr
to the current window. -
Character Frequency Count: For each character added to the window at
r
, we update its frequency in aCounter
. -
Adjust Left Boundary: When a character's count within the window becomes
k
or more, it means any substring starting beforel
with currentr
satisfies the condition that at least one character appears at leastk
times. Thus, we need to adjust the left boundaryl
to remove characters from the window until the character count falls belowk
. -
Count Substrings: For every valid window configuration where the right boundary ends at
r
, all possible starting points[0..l-1]
generate valid substrings. Therefore, we addl
to our answer, as it represents the number of valid starting points.
This approach efficiently calculates the number of desired substrings by making optimal use of the sliding window technique and character frequency count, avoiding unnecessary recalculations.
Learn more about Sliding Window patterns.
Solution Approach
The solution utilizes the Sliding Window technique coupled with a frequency counter to efficiently find the number of valid substrings. Here's a detailed walkthrough:
-
Initialize Data Structures:
- A
Counter
is used to track the frequency of each character within the current window. - An integer
l
is used to mark the start of the window. - An integer
ans
is initialized to accumulate the total count of valid substrings.
- A
-
Iterate Through the String:
- As we iterate over each character
c
in the string with the right endpointr
, increment the count of this character in theCounter
.
- As we iterate over each character
-
Adjust the Left Boundary:
- If the count of the current character
c
in theCounter
reachesk
, it indicates that the substring from a previous left boundary to currentr
has a character appearingk
or more times. - To ensure all substrings counted are valid, increment the left boundary
l
until the count ofc
becomes less thank
. This is done by decrementing the count of the character at the current left boundary and movingl
one step forward.
- If the count of the current character
-
Count Valid Substrings:
- For every position
r
, all substrings starting between indices[0...l-1]
and ending atr
are valid. Hence, addl
toans
.
- For every position
-
Return the Result:
- After processing all characters,
ans
will hold the total count of substrings where at least one character appears at leastk
times.
- After processing all characters,
This approach is efficient with a time complexity of O(n) due to the linear traversal and adjustment of the window, where n
is the length of the string. It ensures that each character is processed in a constant amount of work per step, leveraging the power of the sliding window pattern.
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 illustrate the solution with a small example where s = "aaabbc"
and k = 2
.
-
Initialize Data Structures:
- We start with an empty
Counter
to track character frequencies, a left pointerl = 0
to mark the start of the sliding window, and anans = 0
to accumulate the count of valid substrings.
- We start with an empty
-
Iterate Through the String:
-
Iteration 1 (r = 0, character = 'a'):
Increase the count of 'a' in theCounter
, resulting in{'a': 1}
. The count is less thank
, so no adjustment ofl
is needed. -
Iteration 2 (r = 1, character = 'a'):
Update the Counter to{'a': 2}
. Now, 'a' appears at leastk
times within the window[0, 1]
. All substrings ending at 1 and starting from[0...0]
are valid. Addl = 1
toans
. Thus,ans = 1
. -
Iteration 3 (r = 2, character = 'a'):
Now, theCounter
is{'a': 3}
. 'a' still appears at leastk
times, so include valid substrings ending at 2 and starting from[0...0]
. Addl = 1
toans
, makingans = 2
. -
Iteration 4 (r = 3, character = 'b'):
Update theCounter
to{'a': 3, 'b': 1}
. 'b' does not meetk
yet, so there's no need to adjustl
. Addl = 1
toans
, resulting inans = 3
as substrings[0...0]
to 3 are valid. -
Iteration 5 (r = 4, character = 'b'):
TheCounter
becomes{'a': 3, 'b': 2}
. Now, 'b' appearsk
times. Adjustl
to ensure valid substrings because a character reaches its limit. Addl = 1
toans
, makingans = 4
. -
Iteration 6 (r = 5, character = 'c'):
Update theCounter
to{'a': 3, 'b': 2, 'c': 1}
. Neither 'c' nor 'a' is adjusted to needl
changes, so addl = 1
to theans
, resulting inans = 5
.
-
-
Return the Result:
- After iterating through the string, the total number of valid substrings is stored in
ans
, which is5
.
- After iterating through the string, the total number of valid substrings is stored in
This example showcases how the sliding window and frequency counter work together to efficiently count substrings where at least one character appears at least k
times.
Solution Implementation
1from collections import Counter
2
3class Solution:
4 def numberOfSubstrings(self, s: str, k: int) -> int:
5 # Initialize a Counter to keep track of character frequencies
6 character_count = Counter()
7
8 # Initialize answer and the left pointer of the sliding window
9 answer = 0
10 left_pointer = 0
11
12 # Iterate over each character in the string
13 for character in s:
14 # Increment the frequency count for the current character
15 character_count[character] += 1
16
17 # If the frequency of the current character is at least k
18 while character_count[character] >= k:
19 # Decrement the frequency of the character at the left pointer
20 character_count[s[left_pointer]] -= 1
21 # Move the left pointer to the right
22 left_pointer += 1
23
24 # Add the number of valid starting positions for substrings ending at current position
25 answer += left_pointer
26
27 # Return the accumulated answer
28 return answer
29
1class Solution {
2 public long numberOfSubstrings(String s, int k) {
3 int[] characterCount = new int[26]; // Array to count occurrences of each character
4 long result = 0; // Variable to store the final result (number of substrings)
5
6 // Two pointers approach: 'left' and 'right'
7 for (int left = 0, right = 0; right < s.length(); ++right) {
8 int currentCharIndex = s.charAt(right) - 'a'; // Convert character to index
9 ++characterCount[currentCharIndex]; // Increase count for the current character
10
11 // Check if the current character count is at least 'k'
12 while (characterCount[currentCharIndex] >= k) {
13 --characterCount[s.charAt(left) - 'a']; // Decrease count for character at 'left'
14 left++; // Move the left pointer for a new potential start of the substring
15 }
16
17 // Add 'left' to the result, this represents all valid substrings ending at 'right'
18 result += left;
19 }
20
21 return result; // Return the total count of valid substrings
22 }
23}
24
1class Solution {
2public:
3 long long numberOfSubstrings(string s, int k) {
4 // n is the length of the input string s
5 int n = s.size();
6 // ans will store the total number of valid substrings
7 long long ans = 0;
8 // l is the left pointer of the sliding window
9 int l = 0;
10 // cnt is an array to keep track of the count of each character in the current window
11 int cnt[26]{}; // Initialize count array to 0 for each of the 26 lowercase letters
12
13 // Iterate over each character in the string s
14 for (char& c : s) {
15 // Increment the count for the current character
16 ++cnt[c - 'a'];
17
18 // Shift the left pointer l until the current character count is less than k
19 while (cnt[c - 'a'] >= k) {
20 // Decrement the count of the character at the current left position
21 --cnt[s[l++] - 'a'];
22 }
23
24 // Add the number of valid substrings ending at the current position
25 ans += l;
26 }
27
28 // Return the total number of valid substrings
29 return ans;
30 }
31};
32
1/**
2 * This function calculates the number of substrings where each character appears at least k times.
3 * @param s - The input string consisting only of lowercase English letters.
4 * @param k - The minimum number of times each character should appear in a substring.
5 * @returns The total number of valid substrings.
6 */
7function numberOfSubstrings(s: string, k: number): number {
8 let ans = 0; // Initialize result to count valid substrings.
9 let l = 0; // Left pointer for the sliding window.
10 const count: number[] = Array(26).fill(0); // Array to keep the count of each character in the window.
11
12 // Iterate over each character in the string.
13 for (const char of s) {
14 const currentIndex = char.charCodeAt(0) - 97; // Map char to 0-based index (a=0, b=1, ..., z=25).
15 count[currentIndex]++; // Increment the count for the current character.
16
17 // Check if the count of the current character meets or exceeds k.
18 while (count[currentIndex] >= k) {
19 // If so, contract the window from the left.
20 count[s[l++].charCodeAt(0) - 97]--; // Decrease count of the character at the left pointer and move left pointer.
21 }
22
23 ans += l; // Add the number of valid substrings ending at the current position.
24 }
25
26 return ans; // Return the total number of valid substrings.
27}
28
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 at most twice. The outer loop processes each character once, and the inner while loop ensures that each character in the subarray increasing l
is also processed at most once. Thus, the entire string is traversed with linear complexity.
The space complexity is O(|Σ|)
, where Σ
is the character set, which in the given context is the set of lowercase English letters. This results in |Σ| = 26
, as there are 26 possible characters at most, and the Counter
object stores these counts, making the space complexity constant at O(26)
.
Learn more about how to find time and space complexity quickly.
Which of the following is a min heap?
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!