題目大意
給你一連串的操作,L, R分別代表移動光標操作,其他字符表示在當前光標位置插入一個字符。如果該位置有字符,那麼進行替換。讓你判斷當前輸出的序列是否合法,如果合法,那麼括號的最大嵌套深度是多少?
思路
首先這個序列的長度不會超過的長度,對於(我們可以看成在該位置加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;
}