解法
操作を逆に行う. 文字列\(S\)からスタートして,\(S\)を ##...# にする. # はワイルドカードだと思ってよい. つまり,何が入っていいても良い文字と考える.
\(S\)において,文字列 \(T\) を連続部分列として含むかどうかを判定して, 含む場合は # で置き換える. # がワイルドカードであることから, 置き換えが可能な場所はすぐに置き換えてよい.
次に,更新すべき箇所を減らすことを考える. \(S\)における \(i\)番目から \(T\)で置き換えた場合, 同じ \(i\) に対しては操作をする必要がない. また,# で置き換えた場合,新しく置き換えが出来る場所の候補として 追加されるのは,置き換えた # の近くだけである. 例えば,\(M=3\)のとき,
_____###_____
___ooooooo___
の様に o が置かれた場所だけが,次に置き換わる可能性のある場所.
実装の注意
used は,push 出来た場合だけ true にする. true にするタイミングを間違うとWA.
使っている記号,マクロ等 "https://ecsmtlir.hatenablog.com/entry/2022/12/23/131925"
int main() {
ll n,m;
cin >> n >> m;
string s,t ; cin >> s >> t;
queue<ll> q;
vll used(n);
auto push = [&](ll i) -> void {
if(!in(i,n)) return;
if(i+m > n) return;
if(used[i]) return;
rep(j,m){
if(s[j+i]!='?' && s[j+i]!=t[j]) return;
}
rep(j,m){
s[j+i] = '?';
}
q.push(i);
used[i] = true; // push が成功したときだけ used
};
rep(i,n) push(i);
while(q.size()){
ll cu = q.front(); q.pop();
srep(i,cu-m+1,cu+m){
push(i);
}
}
for(char c: s) if(c != '?') { cout << "No" << endl; return 0;}
cout << "Yes" << endl;
return 0;
}