【競技プログラミング】AtCoder Beginner Contest 372_C問題

# 問題
https://atcoder.jp/contests/abc372/tasks/abc372_c

# 既存投稿一覧ページへのリンク
[一覧ページ](https://qiita.com/o_chan_t/items/fc759695acfaef8df2cc)

## Before
[AtCoder Beginner Contest 371_E 問題](https://qiita.com/o_chan_t/items/3e9998fcf348bb9f13da)

# 解法手順1

## ステップ1：初期状態で "ABC" の個数をカウントする

まず、文字列 $ S $ の中に "ABC" という連続部分文字列がいくつあるかを数える。  
これは、長さ $ N $ の文字列 $ S $ の各位置 $ i $（ただし $ 0 \leq i \leq N-3 $）について、  
$ S[i], S[i+1], S[i+2] $ がそれぞれ 'A', 'B', 'C' ならカウントを1増やす。

```python
abc_count = 0
for i in range(N - 2):
    if S[i] == "A" and S[i+1] == "B" and S[i+2] == "C":
        abc_count += 1
```

---

## ステップ2：クエリごとに処理を繰り返す

各クエリでは、指定された位置の文字を変更し、そのたびに "ABC" の個数を更新して出力する。

---

### 2-1. 変更前の "ABC" を減算

変更する位置 $ X $ の前後で "ABC" が消える可能性があるので、$ X-2 $ から $ X $ までの3か所（ただし範囲外は除く）を調べ、もし "ABC" があればカウントを1減らす。

```python
for k in range(3):
    pos = Xi - k
    if 0 <= pos and pos + 2 < N:
        if S[pos] == "A" and S[pos+1] == "B" and S[pos+2] == "C":
            abc_count -= 1
```

---

### 2-2. 文字列を更新

指定された位置の文字を新しい文字に置き換える。

```python
S[Xi] = Ci
```

---

### 2-3. 変更後の "ABC" を加算

再び、$ X-2 $ から $ X $ までの3か所を調べ、今度は新しく "ABC" が現れた場所があればカウントを1増やす。

```python
for k in range(3):
    pos = Xi - k
    if 0 <= pos and pos + 2 < N:
        if S[pos] == "A" and S[pos+1] == "B" and S[pos+2] == "C":
            abc_count += 1
```

---

### 2-4. 現在の "ABC" の個数を出力

```python
print(abc_count)
```

---

## まとめ：全体の流れ

![base_image.create_20250420000806-ページ4.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/530912/461d4ddf-ec0e-415b-82d0-d0e947097649.png)


# ACコード1
```python:ac.py
import logging

def setup_logger(debug_mode):
    logger = logging.getLogger(__name__)
    if not logger.handlers:
        logger.setLevel(logging.DEBUG if debug_mode else logging.INFO)
        formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
        file_handler = logging.FileHandler('program_trace.log', encoding="utf8")
        file_handler.setFormatter(formatter)
        logger.addHandler(file_handler)
    logger.debug(f"ロガーのセットアップが完了しました。デバッグモード: {debug_mode}")
    return logger

def io_func():
    # 入力の受け取り
    N, Q = map(int, input().split())
    S = list(input())
    queries = []
    for _ in range(Q):
        Xi, Ci = input().split()
        queries.append((int(Xi) - 1, Ci))
    return N, Q, S, queries

def solve(N, Q, S, queries, logger):
    # 初期状態で"ABC"が何個あるかカウント
    abc_count = 0
    for i in range(N - 2):
        if S[i] == "A" and S[i+1] == "B" and S[i+2] == "C":
            abc_count += 1
    logger.debug(f"初期状態での'ABC'の個数: {abc_count}")

    for idx, (Xi, Ci) in enumerate(queries):
        logger.debug(f"クエリ{idx+1}: 位置={Xi+1}, 変更後文字='{Ci}'")
        # 変更前、影響範囲の"ABC"を減算
        for k in range(3):
            pos = Xi - k
            if 0 <= pos and pos + 2 < N:
                if S[pos] == "A" and S[pos+1] == "B" and S[pos+2] == "C":
                    abc_count -= 1
                    logger.debug(f"変更前: S[{pos}:{pos+3}]='{S[pos]}{S[pos+1]}{S[pos+2]}' -> 'ABC'減算 (新カウント: {abc_count})")
        # 文字列更新
        old_char = S[Xi]
        S[Xi] = Ci
        logger.debug(f"S[{Xi}]を'{old_char}'から'{Ci}'に変更")
        # 変更後、影響範囲の"ABC"を加算
        for k in range(3):
            pos = Xi - k
            if 0 <= pos and pos + 2 < N:
                if S[pos] == "A" and S[pos+1] == "B" and S[pos+2] == "C":
                    abc_count += 1
                    logger.debug(f"変更後: S[{pos}:{pos+3}]='{S[pos]}{S[pos+1]}{S[pos+2]}' -> 'ABC'加算 (新カウント: {abc_count})")
        print(abc_count)

def main():
    logger = setup_logger(debug_mode=True)
    N, Q, S, queries = io_func()
    solve(N, Q, S, queries, logger)

if __name__ == "__main__":
    main()

```