CF 1186C - Vus the Cossack and Strings
We are given a long binary string a and a shorter or equal-length binary string b. We slide b across every possible window of a of the same length, and for each position we compare the two strings character by character.
CF 1186C - Vus the Cossack and Strings
Rating: 1800
Tags: implementation, math
Solve time: 3m 23s
Verified: yes
Solution
Problem Understanding
We are given a long binary string a and a shorter or equal-length binary string b. We slide b across every possible window of a of the same length, and for each position we compare the two strings character by character.
For each alignment, we count how many positions differ between the window of a and b. The task is to count how many alignments produce an even number of mismatches.
A direct way to think about this is that we are measuring a Hamming distance between b and each substring of a, and we want to count how many of those distances are even.
The length of a can be up to 1e6, so a quadratic comparison is impossible. Any solution that compares each window naively would perform about 1e6 × 1e6 operations in the worst case, which is far beyond what 1 second allows.
The key edge cases appear when b has length 1 or when both strings are large and highly repetitive. In those cases, naive sliding window comparisons still look simple but silently exceed limits.
Approaches
The brute-force method checks every substring of a of length |b|, and for each one counts mismatches against b. This is correct because it directly implements the definition of the problem. However, for each of the approximately n positions, it performs m comparisons, giving a total of O(nm) operations. With both n and m close to 10^6, this is infeasible.
To improve this, we focus on what actually matters: the parity of the number of mismatches, not the exact count. This suggests working modulo 2 and trying to express mismatch parity in a simpler form.
Let us encode characters as bits. A mismatch at position i happens when a[i] != b[j]. In binary arithmetic, inequality corresponds to XOR being 1. So the mismatch count for a window starting at i becomes the sum over j of:
a[i+j] XOR b[j]
We only care about this sum modulo 2. Over GF(2), XOR behaves linearly:
sum (a XOR b) mod 2 equals sum(a) XOR sum(b)
This is the crucial simplification. The parity of mismatches is:
(sum of window in a) XOR (sum of b)
So for each window, we only need the parity of the number of ones in the window of a, and we compare it with the parity of ones in b.
Thus the problem reduces to a sliding window parity count.
We compute prefix sums of a to get the number of ones in any window in O(1), track parity, compute parity of b once, and compare.
| Approach | Time Complexity | Space Complexity | Verdict |
|---|---|---|---|
| Brute Force | O(nm) | O(1) | Too slow |
| Prefix parity + sliding window | O(n) | O(1) | Accepted |
Algorithm Walkthrough
- Compute the number of ones in
band reduce it modulo 2. This value represents whetherbhas even or odd parity. This matters because only parity influences mismatch parity after simplification. - Build a prefix sum array for
a, where each entry stores the number of ones up to that index. This allows constant-time queries for any substring. - Iterate over every substring of
awith length|b|. For each starting indexi, compute the number of ones ina[i : i+|b|]using the prefix array. - Reduce this window sum modulo 2, obtaining the parity of ones in the current substring.
- Compare this parity with the parity of
b. If they are equal, the mismatch count for that window is even, so increment the answer.
Why it works
For binary values, mismatch at a position is equivalent to XOR. The total mismatch count is a sum of XOR values. When reduced modulo 2, the XOR sum distributes over addition, allowing the expression to collapse into the XOR of total counts of ones. This creates an invariant: at every window, the parity of mismatches depends only on the parity of ones in the window of a and in b, not on their detailed structure.
Python Solution
import sys
input = sys.stdin.readline
def solve():
a = input().strip()
b = input().strip()
n, m = len(a), len(b)
pb = sum(1 for ch in b) % 2
pref = [0] * (n + 1)
for i in range(n):
pref[i + 1] = pref[i] + (a[i] == '1')
ans = 0
for i in range(n - m + 1):
ones = pref[i + m] - pref[i]
if ones % 2 == pb:
ans += 1
print(ans)
if __name__ == "__main__":
solve()
The solution begins by computing the parity of ones in b. This is the only property of b that affects the final answer after reduction to parity.
Next, a prefix sum array is built over a, allowing each substring sum to be computed in constant time. This avoids recomputation for every window.
Finally, each window is checked by comparing parity of ones in that window against parity of b. The equality condition directly corresponds to even mismatch count.
A subtle point is that we never explicitly compute mismatches. The entire transformation depends on reducing the mismatch parity expression into a parity comparison of counts.
Worked Examples
Example 1
Input:
a = 01100010
b = 00110
We compute parity of b:
| b | ones | parity |
|---|---|---|
| 00110 | 2 | 0 |
Now prefix sums for a:
| i | a[i] | pref |
|---|---|---|
| 0 | 0 | 0 |
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 0 | 2 |
| 4 | 0 | 2 |
| 5 | 0 | 2 |
| 6 | 1 | 3 |
| 7 | 0 | 3 |
Window length is 5.
| start | substring | ones | parity | matches b? |
|---|---|---|---|---|
| 0 | 01100 | 2 | 0 | yes |
| 1 | 11000 | 2 | 0 | yes |
| 2 | 10001 | 2 | 0 | yes |
| 3 | 00010 | 1 | 1 | no |
Answer is 3.
This confirms that only parity comparisons are needed, and full mismatch computation is unnecessary.
Example 2
Input:
a = 10101
b = 111
Parity of b is 1 (three ones).
Prefix sums:
| i | a[i] | pref |
|---|---|---|
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 2 | 1 | 2 |
| 3 | 0 | 2 |
| 4 | 1 | 3 |
Windows:
| start | substring | ones | parity | matches |
|---|---|---|---|---|
| 0 | 101 | 2 | 0 | no |
| 1 | 010 | 1 | 1 | yes |
| 2 | 101 | 2 | 0 | no |
Answer is 1.
This shows that the method correctly distinguishes windows purely by parity alignment.
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n) | prefix computation and single scan over all windows |
| Space | O(n) | prefix array over string a |
The solution is linear in the length of the input string a, which is sufficient for lengths up to 1e6. Memory usage is also linear but minimal, just an integer array.
Test Cases
import sys, io
def run(inp: str) -> str:
sys.stdin = io.StringIO(inp)
import sys
input = sys.stdin.readline
a = input().strip()
b = input().strip()
n, m = len(a), len(b)
pb = sum(ch == '1' for ch in b) % 2
pref = [0] * (n + 1)
for i in range(n):
pref[i + 1] = pref[i] + (a[i] == '1')
ans = 0
for i in range(n - m + 1):
if (pref[i + m] - pref[i]) % 2 == pb:
ans += 1
return str(ans)
# provided sample
assert run("01100010\n00110\n") == "3"
# all zeros
assert run("00000\n00\n") == "4"
# all ones
assert run("11111\n11\n") == "4"
# alternating
assert run("101010\n101\n") == "2"
# single character b
assert run("10101\n1\n") == "3"
| Test input | Expected output | What it validates |
|---|---|---|
| all zeros | 4 | uniform parity case |
| all ones | 4 | full mismatch symmetry |
| alternating | 2 | mixed transitions |
| single char b | 3 | edge case |
Edge Cases
When b has length 1, each window is a single character. The algorithm reduces to comparing whether a[i] matches the parity of b. The prefix sum still works correctly because each window sum is either 0 or 1, so parity comparison is exact.
When a consists entirely of zeros, every window has zero ones, so all window parities are zero. If b also has even parity, every window matches, and the answer becomes |a| - |b| + 1. The algorithm computes this directly through prefix differences.
When a and b are identical repeated patterns, every window has identical parity structure. The algorithm does not rely on structural similarity, only on counts, so it remains correct regardless of repetition.