Search for a command to run...
Progressive hints first, then the full explanation and implementation when you're ready to cash out.
Review status
AI-generated and still unreviewed. Double-check the details before internalizing them.
Hints
Open only as much as you need to keep the solve alive.
One operation is one contiguous block of . So the real subproblem is: for a fixed start in , how far can one operation extend?
A block is valid iff some substring of of the same length differs from it in at most one position.
For a fixed start in , maintain two sets of starting positions in : exact matches of , and matches with at most one mismatch.
When appending a new character , exact matches must also have at the new aligned position. Fuzzy matches either already used their one mismatch and need to match, or were exact before and can spend the mismatch now.
Represent those sets as bitsets over positions of . The transition is and , where marks aligned occurrences of the next character and marks starts where the longer substring still fits.
Research note: I checked the official Codeforces editorial, the Codeforces accepted-status listing, and maspy's round notes. The official page exposes solution code but no prose tutorial for E1/E2; maspy explicitly describes the bitset approach. The supplied statement is still the source of truth.
Call a string block good if it can be produced in one operation. For any position in , define as the maximum length such that is good.
We greedily append the longest possible block from the current position. Generic greedy would be suspicious here, but this problem has the needed monotonicity. If is good, then is also good: take the same source substring from , just without its first character. If the removed character was the modified one, then the suffix has zero mismatches. Therefore the optimal number of operations for suffixes of is nonincreasing as the starting index moves right. Landing farther right cannot make the rest harder. So choosing is optimal.
Now compute .
For fixed and current length , maintain two sets of starting positions in :
For , every start in is both exact and fuzzy.
Suppose the next target character is . Let:
Then:
and
Why? For a length- fuzzy match, either the previous characters already had at most one mismatch and the new character matches, or the previous characters were exact and we spend the one allowed change on the new character. That's the whole trick.
Represent each set as a bitset over starts in . Precompute , where bit is set iff . Then is shifted right by , because bit should inspect . The set is the prefix mask containing the first bits.
For a fixed , keep extending while is nonempty. The last successful is . Then move and count one operation.
Single-character blocks are always possible because : copy any one character from and change it if needed. So the answer always exists.
Each extension scans machine words. Across the greedy process, the tested prefixes cover successful extensions plus at most one failed extension per operation, so the total time is
per test case, with memory.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
ull prefixWord(int cnt, int word) {
int left = word * 64;
if (cnt <= left) return 0;
if (cnt - left >= 64) return ~0ULL;
return (1ULL << (cnt - left)) - 1ULL;
}
ull shiftedWord(const vector<ull>& bits, int shift, int word) {
int wordShift = shift / 64;
int bitShift = shift % 64;
int idx = word + wordShift;
ull res = 0;
if (idx < (int)bits.size()) res |= bits[idx] >> bitShift;
if (bitShift && idx + 1 < (int)bits.size()) {
res |= bits[idx + 1] << (64 - bitShift);
}
return res;
}
int bestPrefix(
int start,
const string& target,
int n,
const array<vector<ull>, 26>& occ,
vector<ull>& exact,
vector<ull>& fuzzy,
vector<ull>& nextExact,
vector<ull>& nextFuzzy
) {
int words = (int)exact.size();
for (int w = 0; w < words; ++w) {
ull allStarts = prefixWord(n, w);
exact[w] = allStarts;
fuzzy[w] = allStarts;
}
int best = 0;
for (int len = 0; start + len < (int)target.size(); ++len) {
int validCount = n - len;
if (validCount <= 0) break;
int c = target[start + len] - 'a';
bool any = false;
for (int w = 0; w < words; ++w) {
ull match = shiftedWord(occ[c], len, w);
ull valid = prefixWord(validCount, w);
nextExact[w] = exact[w] & match;
nextFuzzy[w] = (fuzzy[w] & match) | (exact[w] & valid);
any |= nextFuzzy[w] != 0;
}
if (!any) break;
exact.swap(nextExact);
fuzzy.swap(nextFuzzy);
best = len + 1;
}
return best;
}
int main() {
setIO();
int tc;
cin >> tc;
while (tc--) {
int n, m;
string s, t;
cin >> n >> m >> s >> t;
int words = (n + 63) / 64;
array<vector<ull>, 26> occ;
for (auto& v : occ) v.assign(words, 0);
for (int i = 0; i < n; ++i) {
occ[s[i] - 'a'][i / 64] |= 1ULL << (i % 64);
}
vector<ull> exact(words), fuzzy(words), nextExact(words), nextFuzzy(words);
int ans = 0;
for (int pos = 0; pos < m; ) {
int len = bestPrefix(pos, t, n, occ, exact, fuzzy, nextExact, nextFuzzy);
pos += len;
++ans;
}
cout << ans << '\n';
}
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
ull prefixWord(int cnt, int word) {
int left = word * 64;
if (cnt <= left) return 0;
if (cnt - left >= 64) return ~0ULL;
return (1ULL << (cnt - left)) - 1ULL;
}
ull shiftedWord(const vector<ull>& bits, int shift, int word) {
int wordShift = shift / 64;
int bitShift = shift % 64;
int idx = word + wordShift;
ull res = 0;
if (idx < (int)bits.size()) res |= bits[idx] >> bitShift;
if (bitShift && idx + 1 < (int)bits.size()) {
res |= bits[idx + 1] << (64 - bitShift);
}
return res;
}
int bestPrefix(
int start,
const string& target,
int n,
const array<vector<ull>, 26>& occ,
vector<ull>& exact,
vector<ull>& fuzzy,
vector<ull>& nextExact,
vector<ull>& nextFuzzy
) {
int words = (int)exact.size();
for (int w = 0; w < words; ++w) {
ull allStarts = prefixWord(n, w);
exact[w] = allStarts;
fuzzy[w] = allStarts;
}
int best = 0;
for (int len = 0; start + len < (int)target.size(); ++len) {
int validCount = n - len;
if (validCount <= 0) break;
int c = target[start + len] - 'a';
bool any = false;
for (int w = 0; w < words; ++w) {
ull match = shiftedWord(occ[c], len, w);
ull valid = prefixWord(validCount, w);
nextExact[w] = exact[w] & match;
nextFuzzy[w] = (fuzzy[w] & match) | (exact[w] & valid);
any |= nextFuzzy[w] != 0;
}
if (!any) break;
exact.swap(nextExact);
fuzzy.swap(nextFuzzy);
best = len + 1;
}
return best;
}
int main() {
setIO();
int tc;
cin >> tc;
while (tc--) {
int n, m;
string s, t;
cin >> n >> m >> s >> t;
int words = (n + 63) / 64;
array<vector<ull>, 26> occ;
for (auto& v : occ) v.assign(words, 0);
for (int i = 0; i < n; ++i) {
occ[s[i] - 'a'][i / 64] |= 1ULL << (i % 64);
}
vector<ull> exact(words), fuzzy(words), nextExact(words), nextFuzzy(words);
int ans = 0;
for (int pos = 0; pos < m; ) {
int len = bestPrefix(pos, t, n, occ, exact, fuzzy, nextExact, nextFuzzy);
pos += len;
++ans;
}
cout << ans << '\n';
}
}