Official
E - Duplicate Editorial by Nyaan
はじめに、\(S\) の中で 2
以上の数字が隣り合っている箇所があったら答えは -1
です。
そうでない場合を考えます。たとえば \(S=\) 1121131
とします。操作を行うごとに S
は以下のように変化します。
1121131
111211113
11112111111
11111211111
111111211111
\(\vdots\)
列を観察すると次のような事実が分かります。
- 末尾に連なる連続した同じ数字 (上の例での
3
や111111
) は長さが 1 ずつ減っていきやがて無くなる。 - 末尾でない箇所の
2
から9
は個数が変化しない。 - 末尾でない箇所の連続する
1
は, 直後の数字の値を \(x\) として \(x-1\) 個ずつ増えていく。 例えば先頭の11
は, その直後の数字の値が \(2\) なので \(2-1=1\) 個ずつ増える。
この法則のポイントは「同じ数字が連続する列は末尾の要素しか減っていかない」という点です。これを利用することで、\(S\) を連長圧縮 (Run Length Encoding) した列を持ち、連長圧縮した列の末尾を pop しながら \(S\) の文字数・操作回数をシミュレーションするアルゴリズムで解くことができるとわかります。(詳細は実装例を参照ください)
計算量は \(\mathrm{O}(N)\) で十分高速です。
- 実装例(C++)
#include <bits/stdc++.h>
using namespace std;
#include "atcoder/modint.hpp"
using mint = atcoder::modint998244353;
vector<pair<char, int>> RunLengthEncoding(string& S) {
if (S.empty()) return {};
vector<pair<char, int>> ret;
char c = S[0];
int n = 1;
for (int i = 1; i < (int)S.size(); i++) {
if (S[i] == c)
n++;
else {
ret.emplace_back(c, n);
c = S[i], n = 1;
}
}
ret.emplace_back(c, n);
return ret;
}
int main() {
int N;
string S;
cin >> N >> S;
for (int i = 0; i + 1 < N; i++) {
if (S[i] >= '2' and S[i + 1] >= '2') {
cout << "-1\n";
exit(0);
}
}
auto rle = RunLengthEncoding(S);
mint t = 0;
char last = '1';
while (!rle.empty()) {
char c;
mint n;
tie(c, n) = rle.back();
rle.pop_back();
if (c == '1') {
n += t * (last - '1');
t += n;
} else {
t += 1;
}
last = c;
}
cout << (t - 1).val() << "\n";
}
posted:
last update: