ホーム
/
作ったもの
/
考えたこと
/
きろく

003 - AB → BAA 解説

実行時間制限: 2sec / メモリ制限: 1024 MB

解説

 操作 '$\text{AB}$' $\to$ '$\text{BAA}$' を何度行っても、文字列中の '$\text{B}$' の数は変化しません。したがって、$S$ と $T$ で '$\text{B}$' の数が異なる場合は '$\text{No}$' です。

 '$\text{B}$' を仕切りとして文字列をブロックに分けると、操作は「左のブロックの '$\text{A}$' を $1$ つ消費し、右隣のブロックに '$\text{A}$' を $2$ つ生成する」ことと同等になります。'$\text{A}$' は左から右へしか移動・増殖できないため、左のブロックから順に貪欲に処理するのが最善です。

 $S$ と $T$ を '$\text{B}$' で分割した各ブロックの '$\text{A}$' の個数について、左から順に以下のように貪欲法を行います。

  • 現在のブロックで $S$ 側の '$\text{A}$' が不足する場合(右から持ってきて補う手段はないため)即座に '$\text{No}$' と判定する。
  • 余った '$\text{A}$' は、すべて $2$ 倍にして次の右隣のブロックへ持ち越す。

 なお、持ち越す '$\text{A}$' の数は指数関数的に増える可能性がありますが、$T$ の長さである $2 \times 10^5$ を超えた分は打ち切って(上限をかけて)問題ありません。最後のブロックまで処理して余りがぴったり $0$ になれば '$\text{Yes}$'、余っていれば '$\text{No}$' です。計算量は $O(|S| + |T|)$ となります。

コード例

以下はC++による想定解法のコードです。

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    string S, T;
    if (!(cin >> S >> T)) return 0;
    
    if (count(S.begin(), S.end(), 'B') != count(T.begin(), T.end(), 'B')) {
        cout << "No\n";
        return 0;
    }
    
    auto get_a = [](const string& s) {
        vector<long long> a;
        long long cnt = 0;
        for (char c : s) {
            if (c == 'A') cnt++;
            else { a.push_back(cnt); cnt = 0; }
        }
        a.push_back(cnt);
        return a;
    };
    
    auto A = get_a(S), B = get_a(T);
    long long rem = 0, LIMIT = 300000;
    
    for (size_t i = 0; i < A.size(); ++i) {
        rem = rem * 2 + A[i] - B[i];
        if (rem < 0) {
            cout << "No\n";
            return 0;
        }
        rem = min(rem, LIMIT);
    }
    
    cout << (rem == 0 ? "Yes\n" : "No\n");
    return 0;
}