题目大意
给你一连串的操作,L, R分别代表移动光标操作,其他字符表示在当前光标位置插入一个字符。如果该位置有字符,那么进行替换。让你判断当前输出的序列是否合法,如果合法,那么括号的最大嵌套深度是多少?
思路
首先这个序列的长度不会超过
1
0
6
10^6
106的长度,对于(我们可以看成在该位置加1,对于)我们可以看成在该位置减一,然后我们对该序列求前缀和,因为需要保证输出的括号序列合法,那么需要判断前缀和中是否有小于0的数以及整个序列的前缀和是否等于0,如果有,该序列不合法。除此之外。除此之外我们还需要维护前缀和的最大值,因为最大值对应着括号的最大嵌套深度。
于是,这就是个线段树的 区间更新的题目,线段树的每个点维护前缀和。对于每次更改根据需要更新[当前光标,n]里面所有前缀和的最大值和最小值。
代码1
#include <bits/stdc++.h>
using namespace std;
#define mid (L+R)/2
#define lson o<<1, L, mid
#define rson o<<1|1, mid+1, R
const int maxn = 1e6+100;
struct SegmentTree{
int pmin[maxn<<2], pmax[maxn<<2], lazy[maxn<<2];
void pushdown(int o){
if(lazy[o]){
lazy[o<<1] += lazy[o], lazy[o<<1|1] += lazy[o];
pmax[o<<1] += lazy[o], pmin[o<<1] += lazy[o];
pmax[o<<1|1] += lazy[o], pmin[o<<1|1] += lazy[o];
lazy[o] = 0;
}
}
void pushup(int o){
pmax[o] = max(pmax[o<<1], pmax[o<<1|1]);
pmin[o] = min(pmin[o<<1], pmin[o<<1|1]);
}
void update(int o, int L, int R, int l, int r, int val){
if(L >= l && R <= r){
lazy[o] += val, pmax[o] += val, pmin[o] += val;
return;
}
pushdown(o);
if(l <= mid) update(lson, l, r, val);
if(r > mid) update(rson, l, r, val);
pushup(o);
}
}tree;
char s[maxn], now[maxn];
int ans[maxn];
int main(){
int n; scanf("%d %s", &n, s);
int cursor = 1, sum = 0;
for(int i = 0; i < n; i++){
if(s[i] == 'R') cursor++;
else if(s[i] == 'L') cursor = max(cursor-1, 1);
else{
if(s[i] == '('){
if(now[cursor] == ')') sum += 2, tree.update(1, 1, n, cursor, n , 2);
else if(now[cursor] == '(');
else sum++, tree.update(1, 1, n, cursor, n, 1);
}
else if(s[i] == ')'){
if(now[cursor] == ')') ;
else if(now[cursor] == '(') sum -= 2, tree.update(1, 1, n, cursor, n, -2);
else sum--, tree.update(1, 1, n, cursor, n, -1);
}
else{
if(now[cursor] == ')') sum++, tree.update(1, 1, n, cursor, n, 1);
else if(now[cursor] == '(') sum--, tree.update(1, 1, n, cursor, n, -1);
}
now[cursor] = s[i];
}
if(sum == 0 && tree.pmin[1] >= 0) ans[i] = tree.pmax[1];
else ans[i] = -1;
}
for(int i = 0; i < n; i++) printf("%d ", ans[i]);
return 0;
}
supplement
上一部分的代码即臃肿有复杂。还有更简便的方法。我们发现可以将区间更新转化为单点更新。每个点的sum值表示当前区间和,而不是前缀和。pmin和pmax数组也表示当前区间的前缀和的最小值和最大值,而不是从1到当前位置的前缀和。
那么如何进行区间并的操作和呢?具体操作如下:
pmin[o] = min(pmin[o<<1], psum[o<<1] + pmin[o<<1|1]);
pmax[o] = max(pmax[o<<1], psum[o<<1] + pmax[o<<1|1]);
psum[o] = psum[o<<1] + psum[o<<1|1];
因为不用考虑那么多情况直接处理到叶子结点,所以整个代码被压缩的很短。
代码如下:
#include <bits/stdc++.h>
using namespace std;
#define mid (L+R)/2
#define lson o<<1, L, mid
#define rson o<<1|1, mid+1, R
const int maxn = 1e6+100;
struct SegmentTree{
int psum[maxn<<2], pmin[maxn<<2], pmax[maxn<<2];
void pushup(int o){
pmin[o] = min(pmin[o<<1], psum[o<<1] + pmin[o<<1|1]);
pmax[o] = max(pmax[o<<1], psum[o<<1] + pmax[o<<1|1]);
psum[o] = psum[o<<1] + psum[o<<1|1];
}
void update(int o, int L, int R, int p, char val){
if(L == R){
if(val == '(') psum[o] = pmin[o] = pmax[o] = 1;
else if(val == ')') psum[o] = pmin[o] = -1, pmax[o] = 0;
else psum[o] = pmin[o] = pmax[o] = 0;
return ;
}
if(p <= mid) update(lson, p, val);
else update(rson, p, val);
pushup(o);
}
}tree;
char s[maxn];
int ans[maxn];
int main(){
int n; scanf("%d %s", &n, s);
int cursor = 1;
for(int i = 0; i < n; i++){
if(s[i] == 'R') cursor++;
else if(s[i] == 'L') cursor = max(1, cursor-1);
else tree.update(1, 1, n, cursor, s[i]);
if(tree.psum[1] == 0 && tree.pmin[1] >= 0) ans[i] = tree.pmax[1];
else ans[i] = -1;
}
for(int i = 0; i < n; i++) printf("%d ", ans[i]);
return 0;
}