競技プログラミング日記

主に AtCoder の記事です

AtCoder Beginner Contest 329E問題

ABC329E

解法

操作を逆に行う. 文字列\(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;
}