Facebook Pixel

3258. Count Substrings That Satisfy K-Constraint I


Problem Description

You are given a binary string s and an integer k.

A binary string satisfies the k-constraint if either of the following conditions holds:

  • The number of 0s in the string is at most k.
  • The number of 1s in the string is at most k.

Return an integer denoting the number of non-empty substrings of s that satisfy the k-constraint.

Intuition

The solution uses a sliding window approach. The idea is to maintain a window containing a current substring of s and keep track of how many 0s and 1s are present within this window.

The approach is as follows:

  1. Initialize counters for 0s and 1s in the current window. Also, maintain pointers l and r representing the left and right boundary of the window.
  2. Traverse the string character by character. For each character, update the count of 0s or 1s, depending on whether the character is a 0 or a 1.
  3. If both 0 and 1 counts exceed k, keep increasing the left boundary (l) until at least one count is k or smaller. This ensures that the current window satisfies the k-constraint.
  4. Each time the window is shifted, calculate the number of valid substrings from the current valid window, which is the number of positions r - l + 1.
  5. Continue expanding the right boundary until the end of the string.

The algorithm efficiently counts valid substrings while iterating through the string with a time complexity of O(n), where n is the length of s.

Learn more about Sliding Window patterns.

Solution Approach

The solution employs the Sliding Window technique, which is well-suited for problems involving arrays or strings where we need to find a condition that holds for contiguous subarrays or substrings.

  1. Initialization: We begin by initializing two variables cnt and ans. The cnt array stores the count of 0s and 1s in the current window. The variable ans will keep track of the total number of substrings that satisfy the k-constraint. Additionally, a variable l is initialized as 0, which marks the left boundary of our sliding window.
cnt = [0, 0]
ans = l = 0
  1. Window Expansion: Iterate over each character in the binary string s using its index r. Convert the character to an integer (0 or 1) and increase its count in the cnt array.
for r, x in enumerate(map(int, s)):
    cnt[x] += 1
  1. Valid Window Check: After updating the count, check if both counts (cnt[0] and cnt[1]) exceed k. If they do, it means the substring starting from l to r is invalid. Hence, increment the left boundary l until at least one count is k or less.
while cnt[0] > k and cnt[1] > k:
    cnt[int(s[l])] -= 1
    l += 1
  1. Counting Valid Substrings: If the current window satisfies the k-constraint, all substrings starting with an index between l and r are valid. Thus, the total number of such substrings is r - l + 1. Add this count to ans.
ans += r - l + 1
  1. Result: After the loop completes, ans will contain the total number of substrings that fulfill the k-constraint.

Finally, return ans as the result.

return ans

This approach efficiently finds all valid substrings with a time complexity of O(n), iterating through the string a minimal number of times due to the sliding window mechanism.

Ready to land your dream job?

Unlock your dream job with a 2-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through an example to illustrate the solution approach.

Consider the binary string s = "11001" and the integer k = 2.

We'll apply the sliding window technique as follows:

  1. Initialization:

    • cnt = [0, 0] to track the number of 0s and 1s in the window.
    • ans = 0 to accumulate the total number of valid substrings.
    • l = 0 to mark the left boundary of the window.
  2. Window Expansion: Our goal is to iterate over every character in s and expand the window by including each character at the right end r:

    • At r = 0, x = 1: Update cnt to [0, 1].
    • At r = 1, x = 1: Update cnt to [0, 2]. The window is valid as the number of 0s or 1s is less than or equal to k.
    • Count the valid substrings from l = 0 to r = 1. ans = ans + (r - l + 1) = 0 + 2 = 2.
  3. Continue Expanding:

    • At r = 2, x = 0: Update cnt to [1, 2]. Window [0,2] is still valid.
    • Count the valid substrings: ans = 2 + (r - l + 1) = 5.
    • At r = 3, x = 0: Update cnt to [2, 2]. This window [0,3] remains valid.
    • Count the valid substrings: ans = 5 + (r - l + 1) = 9.
  4. Check Constraints:

    • At r = 4, x = 1: Update cnt to [2, 3]. Now, both counts exceed k. We need to increment l until our window is valid.
    • Increment l to 1: cnt becomes [2, 2], making the window [1,4] valid again.
    • Count the valid substrings: ans = 9 + (r - l + 1) = 13.
  5. Final Result:

    • At the end of the iteration, ans = 13 which is the total number of substrings that satisfy the k-constraint.

In this example, we efficiently counted all qualifying substrings using a sliding window, ensuring time complexity of O(n).

Solution Implementation

1class Solution:
2    def countKConstraintSubstrings(self, s: str, k: int) -> int:
3        # Initialize counters for '0' and '1'
4        cnt = [0, 0]
5        # Initialize the answer counter and left pointer 
6        ans = l = 0
7      
8        # Iterate over the string `s` while mapping characters to integers
9        for r, x in enumerate(map(int, s)):
10            # Increase the count of the current character
11            cnt[x] += 1
12          
13            # If both '0' and '1' exceed the allowed count `k`, adjust the left boundary
14            while cnt[0] > k and cnt[1] > k:
15                # Decrease the count of the character at position `l` and move `l` forward
16                cnt[int(s[l])] -= 1
17                l += 1
18          
19            # Add the number of valid substrings ending at position `r`
20            ans += r - l + 1
21      
22        # Return the total count of valid substrings
23        return ans
24
1class Solution {
2    public int countKConstraintSubstrings(String s, int k) {
3        // Array to count occurrences of '0' and '1'
4        int[] count = new int[2]; 
5      
6        int answer = 0; 
7        int left = 0; 
8      
9        // Loop through each character in the string
10        for (int right = 0; right < s.length(); ++right) {
11            // Increase count for the current character
12            ++count[s.charAt(right) - '0'];
13          
14            // Check if both '0' and '1' counts exceed k
15            while (count[0] > k && count[1] > k) {
16                // Reduce count of the character at the left pointer
17                count[s.charAt(left++) - '0']--;
18            }
19          
20            // Calculate the number of valid substrings ending at 'right'
21            answer += right - left + 1;
22        }
23      
24        return answer; // Return total count of substrings
25    }
26}
27
1#include <string> // Include necessary headers for string operations
2
3class Solution {
4public:
5    int countKConstraintSubstrings(std::string s, int k) {
6        // Array to keep track of the count of '0's and '1's in the current window
7        int count[2] = {}; 
8        int answer = 0; // Variable to store the number of valid substrings
9        int left = 0; // Left pointer for the sliding window
10
11        // Iterate over the string with a right pointer
12        for (int right = 0; right < s.length(); ++right) {
13            // Increment the count of the current character in the window
14            count[s[right] - '0']++;
15
16            // Adjust the window to maintain the constraint of at most k '0's and '1's
17            while (count[0] > k && count[1] > k) {
18                // Decrement the count of the character at the left pointer
19                count[s[left++] - '0']--;
20            }
21
22            // Add the number of valid substrings ending at the current character
23            answer += right - left + 1;
24        }
25
26        return answer; // Return the total number of valid substrings
27    }
28};
29
1function countKConstraintSubstrings(s: string, k: number): number {
2    // This array keeps track of the counts of '0's and '1's in the current window.
3    const count: [number, number] = [0, 0];
4  
5    // 'ans' will store the total number of valid substrings,
6    // and 'left' will mark the beginning of the sliding window.
7    let ans = 0;
8    let left = 0;
9
10    // Iterate over each character in the string using 'right' as the end of the sliding window.
11    for (let right = 0; right < s.length; ++right) {
12        // Increment the count of the current character ('0' or '1').
13        count[+s[right]]++;
14      
15        // If both '0's and '1's counts exceed 'k', move the 'left'
16        // pointer to shrink the window until the condition is satisfied.
17        while (count[0] > k && count[1] > k) {
18            count[+s[left]]--;
19            left++;
20        }
21
22        // Add to 'ans' the number of new valid substrings ending at 'right'.
23        ans += right - left + 1;
24    }
25
26    return ans;
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 the algorithm goes through each character of the string exactly once with two pointers l and r iterating over the string, achieving a linear time complexity.

The space complexity is O(1), as the algorithm uses a fixed amount of space regardless of the input size. The space usage includes a list cnt with two elements to count occurrences of 0 and 1, and a few integer variables for processing.

Learn more about how to find time and space complexity quickly.


Discover Your Strengths and Weaknesses: Take Our 2-Minute Quiz to Tailor Your Study Plan:
Question 1 out of 10

Which data structure is used to implement recursion?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!


Load More