E - Stamp Editorial by en_translator
Let us denote by \(S_i\) the \(i\)-th character (0-based) of a string \(S\).
Viewing the operations in the reversed order, we can interpret the problem as follows:
Determine if one can make all the characters of \(S\)
#
by repeatedly applying the following operation against \(S\):
- Choose an \(i\ (0 \leq i < N-M+1)\) such that “\(S_{i+j}=T_j\) or \(S_{i+j}= \)
#
for all \(j\ (0 \leq j < M)\), and replace each of \(S_i, S_{i+1}, \dots, S_{i+M-1}\) with#
.
We say \(i\) is “good” if “\(S_{i+j}=T_j\) or \(S_{i+j}= \)#
for all \(j\ (0 \leq j < M)\). We refer to the operation of replacing each of \(S_i, S_{i+1}, \dots, S_{i+M-1}\) with #
as simply “performing operation for \(i\).”
This new problem can be solved by a simple greedy algorithm as follows:
- Find a good \(i\) for which an operation is not performed yet.
- If there is such an \(i\), perform operation for that \(i\), and return to 1.. Otherwise, determine the answer by checking if all the characters of \(S\) are
#
.
This is justified because performing operation for the same \(i\) twice is meaningless, and if a good \(i\) is found, performing operation for it does not change the answer.
However, one cannot spend \(O(N)\) time every time you find a good \(i\). So, we consider the following algorithm:
- Prepare a queue \(Q\). Push all the good \(i\)’s in the initial state into \(Q\).
- While \(Q\) is not empty, repeat the following:
- Let \(i'\) be an element popped from \(Q\). Perform operation for \(i'\), and push all \(i\) that has become good by this operation into \(Q\).
- Finally, check if all the characters in \(S\) has become
#
.
An element is popped from the queue at most \(N\) times, and an operation for \(i'\) affects to the goodness of the following \(O(M)\) number of \(i\)’s: \(i'-M+1, i'-M+2, \dots, i'+M-1\). Thus, the problem can be solved in a total of \(O(NM^2)\) or \(O(NM)\) time.
Sample code (C++):
#include<bits/stdc++.h>
using namespace std;
int main() {
int n, m;
string s, t;
cin >> n >> m >> s >> t;
vector<bool> used(n - m + 1);
queue<int> q;
auto check = [&](int i) {
if (used[i]) return;
bool ok = true;
for (int j = 0; j < m; j++) {
ok &= (s[i + j] == '#' or s[i + j] == t[j]);
}
if (ok) {
used[i] = true;
q.push(i);
}
};
for (int i = 0; i < n - m + 1; i++) check(i);
while (!q.empty()) {
int i = q.front();
q.pop();
for (int j = 0; j < m; j++) s[i + j] = '#';
for (int j = max(i - m + 1, 0); j <= min(i + m - 1, n - m); j++) {
check(j);
}
}
cout << (s == string(n, '#') ? "Yes" : "No") << endl;
}
posted:
last update: