けんちょんの競プロ精進記録

競プロの精進記録や小ネタを書いていきます

AtCoder ABC 372 C - Count ABC Again (3Q, 灰色, 350 点)

差分のみ更新していく系の典型問題

問題概要

長さ  N の文字列  S が与えられる。次の  Q 回のクエリに答えよ。

【クエリ】
整数  X と文字  C が与えられるので、 S_{X} C に変更する。その後の文字列  S の中に、"ABC" が何個含まれるかを答えよ。

制約

  •  N, Q \le 2 \times 10^{5}

考えたこと

各クエリでは 1 文字を変更するだけなので、「 S 全体の中で "ABC" が何個あるか」に対する影響は微々たるものなのだ。

そこで、 S_{X} を更新するたびに「"ABC" の個数を改めて数える」のではなく、 S_{X} を更新することで「"ABC" の個数がどのように変化するか」を求めていくことにしよう。

次の 2 ステップに分けて考えるとよい。

  • "ABC" が減少する分 ---  S_{X} をたとえば '?' のような適当な文字に書き換えたとしたときに、"ABC" が何個減少するかを求める
  • "ABC" が増加する分 ---  S_{X} を文字  C にしたときに、"ABC" が何個増加するかを求める

これらを求めることで、"ABC" の個数の変化を求めることができる。

"ABC" が減少する分

これは次のように考えられる。

  •  S X-2 文字目から 3 文字が "ABC" であるとき:1 減少する
  •  S X-1 文字目から 3 文字が "ABC" であるとき:1 減少する
  •  S X 文字目から 3 文字が "ABC" であるとき:1 減少する

"ABC" が増加する分

今度は  S_{X} を文字  C に書き換えたあとについて、

  •  S X-2 文字目から 3 文字が "ABC" であるとき:1 増加する
  •  S X-1 文字目から 3 文字が "ABC" であるとき:1 増加する
  •  S X 文字目から 3 文字が "ABC" であるとき:1 増加する

コード

以上の処理を実装すると、 O(N + Q) の計算量となる。

#include <bits/stdc++.h>
using namespace std;

int main() {
    int N, Q, X;
    char C;
    string S;
    cin >> N >> Q >> S;

    int res = 0;
    for (int i = 0; i + 2 < N; i++) if (S.substr(i, 3) == "ABC") res++;
    for (int _ = 0; _ < Q; _++) {   
        cin >> X >> C, X--;

        int deg = 0, add = 0;
        if (X-2 >= 0 && S.substr(X-2, 3) == "ABC") deg++;
        if (X-1 >= 0 && S.substr(X-1, 3) == "ABC") deg++;
        if (S.substr(X, 3) == "ABC") deg++;

        S[X] = C;
        if (X-2 >= 0 && S.substr(X-2, 3) == "ABC") add++;
        if (X-1 >= 0 && S.substr(X-1, 3) == "ABC") add++;
        if (S.substr(X, 3) == "ABC") add++;

        res -= deg, res += add;
        cout << res << endl;
    }
}