CF 1690D - Black and White Stripe
We are given a stripe of tiles, each either white or black, and the goal is to ensure that at least one segment of exactly k consecutive tiles is entirely black.
CF 1690D - Black and White Stripe
Rating: 1000
Tags: implementation, two pointers
Solve time: 1m 24s
Verified: yes
Solution
Problem Understanding
We are given a stripe of tiles, each either white or black, and the goal is to ensure that at least one segment of exactly k consecutive tiles is entirely black. The input provides multiple test cases, each specifying the length of the stripe, the required segment length, and the sequence of tiles. Our task is to compute the minimum number of white tiles we need to repaint to black to achieve a contiguous black segment of length k. If such a segment already exists, the answer is zero.
The constraints allow n to be as large as 200,000, and the sum of all n across test cases also caps at 200,000. This implies that any solution with complexity worse than O(n) per test case will likely time out. Naive O(n*k) approaches are too slow because k can be comparable to n.
Subtle edge cases arise when the required segment length equals the total length, k = n, or when k = 1. For example, a stripe W of length 1 with k=1 requires a single repaint. Another scenario is a fully black stripe of length n with k<n; the answer is zero. Handling these edge cases incorrectly often comes from off-by-one errors in segment indexing or from failing to consider segments starting at the first or last position.
Approaches
The brute-force approach is straightforward: examine every possible contiguous segment of length k, count the number of white tiles in it, and take the minimum. This works because each segment is independent, and the number of repaints is exactly the number of white tiles in that segment. However, the brute-force has complexity O(n_k) because for each of the roughly n-k+1 segments, we iterate over k tiles. In the worst case of n = 2*10^5 and k ≈ n, this would result in approximately 4_10^10 operations, which is infeasible.
The key insight is that the problem exhibits a sliding window property. The number of white tiles in a segment of length k changes predictably as we move the window one position to the right: we subtract the contribution of the tile leaving the window and add the contribution of the tile entering. This lets us compute the number of white tiles in every segment of length k in O(n) time.
The observation that the sum of whites in a sliding window can be updated incrementally reduces the solution from O(n*k) to O(n). We only need to maintain a running count of whites and update it as the window moves.
| Approach | Time Complexity | Space Complexity | Verdict |
|---|---|---|---|
| Brute Force | O(n*k) | O(1) | Too slow |
| Sliding Window | O(n) | O(1) | Accepted |
Algorithm Walkthrough
- Read the number of test cases
t. - For each test case, read
n,k, and the stripe string. - Initialize a counter
current_whitesfor the first window of lengthkby counting the number of white tiles in positions 0 through k-1. - Set
min_whitestocurrent_whites. - Slide the window from position 1 to n-k. For each step, if the tile leaving the window is white, decrement
current_whites. If the tile entering the window is white, incrementcurrent_whites. - Update
min_whitesto the minimum of itself andcurrent_whites. - After sliding through the entire stripe, output
min_whitesas the minimum number of repaints needed.
Why it works: The sliding window invariant guarantees that current_whites always accurately reflects the number of white tiles in the current segment of length k. By iterating over all segments exactly once and keeping track of the minimum, we find the optimal number of repaints.
Python Solution
import sys
input = sys.stdin.readline
t = int(input())
for _ in range(t):
n, k = map(int, input().split())
s = input().strip()
# initial window count
current_whites = sum(1 for c in s[:k] if c == 'W')
min_whites = current_whites
for i in range(1, n - k + 1):
if s[i-1] == 'W':
current_whites -= 1
if s[i+k-1] == 'W':
current_whites += 1
min_whites = min(min_whites, current_whites)
print(min_whites)
The first loop counts whites in the initial segment. Sliding the window one position at a time updates the count efficiently. Boundary handling is critical: the first index leaving the window is i-1, and the new tile entering is i+k-1. Using sum(1 for c in ...) avoids off-by-one errors in counting whites.
Worked Examples
Example 1: n=5, k=3, s="BBWBW"
| Window start | Window content | Whites | min_whites |
|---|---|---|---|
| 0 | BBW | 1 | 1 |
| 1 | BWB | 2 | 1 |
| 2 | WBW | 2 | 1 |
The algorithm correctly finds that repainting 1 tile is sufficient.
Example 2: n=5, k=5, s="BBWBW"
| Window start | Window content | Whites | min_whites |
|---|---|---|---|
| 0 | BBWBW | 2 | 2 |
Only one window exists, so 2 repaints are needed. The algorithm handles the edge of window length equal to the stripe length.
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n) per test case | Each character is processed at most twice: once entering and once leaving the sliding window |
| Space | O(1) | Only a few counters are used; no additional arrays proportional to n |
Given the sum of n across all test cases ≤ 2*10^5, the solution runs comfortably within the 2-second limit.
Test Cases
import sys, io
def run(inp: str) -> str:
sys.stdin = io.StringIO(inp)
output = io.StringIO()
sys.stdout = output
t = int(input())
for _ in range(t):
n, k = map(int, input().split())
s = input().strip()
current_whites = sum(1 for c in s[:k] if c == 'W')
min_whites = current_whites
for i in range(1, n - k + 1):
if s[i-1] == 'W':
current_whites -= 1
if s[i+k-1] == 'W':
current_whites += 1
min_whites = min(min_whites, current_whites)
print(min_whites)
return output.getvalue().strip()
# provided samples
assert run("4\n5 3\nBBWBW\n5 5\nBBWBW\n5 1\nBBWBW\n1 1\nW\n") == "1\n2\n0\n1", "sample tests"
# custom cases
assert run("3\n1 1\nB\n3 2\nWWW\n6 3\nBBBWBB\n") == "0\n2\n0", "edge cases"
assert run("2\n5 5\nBBBBB\n4 2\nWBWB\n") == "0\n1", "full black and alternating"
| Test input | Expected output | What it validates |
|---|---|---|
| 1 1 B | 0 | minimum-length stripe already black |
| 3 2 WWW | 2 | all whites require multiple repaints |
| 6 3 BBBWBB | 0 | segment of length k already exists |
| 5 5 BBBBB | 0 | entire stripe already black, k=n |
| 4 2 WBWB | 1 | alternating pattern, window sliding |
Edge Cases
For n=1, k=1, s="W", the initial window contains a single white tile. The algorithm counts current_whites=1, min_whites=1, and prints 1. This is correct: we must repaint the only tile. No index errors occur because the sliding window loop does not run when n-k=0.
For n=5, k=5, s="BBBBB", the only window already contains zero whites. current_whites=0, min_whites=0. The algorithm outputs 0 correctly without entering the loop.
For alternating patterns like WBWB, current_whites starts at 1 (for the first window). Sliding the window updates counts accurately, confirming that incremental updates handle variable distributions of whites and blacks.