C - Brackets Stack Query Editorial
by
Nyaan
この問題は良い括弧列(バランスの取れた括弧列)が持つ性質を上手く利用する問題です。
良い括弧列は次の性質を持ちます。
文字列 \(S\) が
(
および)
からなる文字列であるとする。
この時、長さ \(|S|+1\) の数列 \(A=(A_0, A_1, \dots, A_{|S|})\) を次のように定義する。ここで \((1 \text{ if } S_i =\)(
\(\text{ else } -1)\) は 「\(S_i\) が(
である時 \(1\)、そうでない時 \(-1\)」という意味で用いている。
- \(A_0 = 0\)
- \(A_1 = A_0 + (1 \text{ if } S_1 =\)
(
\(\text{ else } -1)\)- \(\vdots\)
- \(A_i = A_{i-1} + (1 \text{ if } S_i =\)
(
\(\text{ else } -1)\)- \(\vdots\)
- \(A_{|S|} = A_{|S|-1} + (1 \text{ if } S_{|S|} =\)
(
\(\text{ else } -1)\)この時、\(S\) が良い括弧列である必要十分条件は次の通りである。
- \(A_{|S|} = 0\) かつ \(\min\lbrace A_0, A_1, \dots, A_{|S|} \rbrace = 0\)
上記の性質がぱっとわからない方は、いくつかの括弧列 \(S\) に対して \(A\) を計算してみることで理解の助けになるのではないかと思います。(なお、証明は割愛します)
この上記の性質の \(A\) を stack 状のデータ構造で管理することで今回の問題を解くことが出来ます。
具体的には、今の文字列を \(S\) として、\(B_i = \min \lbrace A_0, A_1, \dots, A_i \rbrace\) と置いた時、stack に
- \(A\) の値 \(A_0, A_1, \dots, A_{|S|}\) と
- \(B\) の値 \(B_0, B_1, \dots, B_{|S|}\)
を管理することにします。
まず、初期状態では \(S\) は空文字列なので \(A_0 = 0\) と \(B_0 = 0\) だけが入っています。
1 番目のクエリで (
を追加するときは、新しい文字列の長さを \(n\) とした時
- \(A_n = A_{n - 1} + 1\)
- \(B_n = \min \lbrace A_0, A_1, \dots, A_n \rbrace = \min \lbrace B_{n-1}, A_n \rbrace\)
が成り立つので、直前の情報から新たな \(A_n, B_n\) を計算できます。(
を追加する場合もほぼ同様です。
2 番目のクエリで \(S\) の末尾の文字を削除する場合は \(A, B\) の末尾の要素を削除すればよいです。
そして、今の文字列が良い括弧列かどうかの判定は \(A_{|S|} = 0\) かつ \(B_{|S|} = 0\) が成り立つかどうかで判定できます。
以上に説明するデータ構造を構成することで今回の問題を解くことが出来ます。計算量は \(\mathrm{O}(Q)\) で十分高速です。
- 実装例(C++)
#include <iostream>
#include <vector>
using namespace std;
int main() {
cin.tie(0)->sync_with_stdio(0);
int Q;
cin >> Q;
vector<int> A{0}, B{0};
for (int q = 1; q <= Q; q++) {
int cmd;
cin >> cmd;
if (cmd == 1) {
char c;
cin >> c;
A.push_back(A.back() + (c == '(' ? 1 : -1));
B.push_back(min(B.back(), A.back()));
} else {
A.pop_back();
B.pop_back();
}
cout << (A.back() == 0 and B.back() == 0 ? "Yes" : "No") << "\n";
}
}
posted:
last update: