「ABC307D」珂朵莉树最強!
题意简述
输入一个字符串,要删掉原字符串中左右匹配的括号及其中的内容。
例如 qwq(qwq)(quq(Q)AQ
的结果就是 qwq(quqAQ
。
思路分析
赛时情况:先写了个括号匹配,然后发现需要标记被删除的区间。拿线段树糊了一个版本,交上去会 TLE,最后也不知道怎么就想到了一个这么可爱的珂朵莉树。然后 AC。上分(x)
本题可以使用「珂朵莉树 + 栈」解决。使用栈进行括号匹配,同时存储上一个左括号出现的位置。若出现右括号,则进行 assign 操作,以标记需要被删除的区间。最后遍历所有区间得到答案。具体参见代码。
代码
#include <iostream>
#include <set>
#include <stack>
using namespace std;
int n, m, u, v, w;
string s, res;
stack<int> st;
/* --- BEGIN ODT --- */
// 抄的 OI Wiki 的模板(x)
struct node {
int l, r;
mutable int v;
node(const int& il, const int& ir, const int& iv)
: l(il)
, r(ir)
, v(iv)
{
}
bool operator<(const node& o) const { return l < o.l; }
};
set<node> odt;
auto split(int x)
{
if (x > n)
return odt.end();
auto it = --odt.upper_bound(node { x, 0, 0 });
if (it->l == x)
return it;
int l = it->l, r = it->r, v = it->v;
odt.erase(it);
odt.insert(node(l, x - 1, v));
return odt.insert(node(x, r, v)).first;
}
void assign(int l, int r, int v)
{
auto itr = split(r + 1), itl = split(l);
odt.erase(itl, itr);
odt.insert(node(l, r, v));
}
/* --- END ODT --- */
int main()
{
cin >> n >> s;
int ptr = 0;
odt.insert({ 1, s.length(), 0 });
while (ptr < s.length()) {
// 括号匹配
if (s[ptr] == '(') {
st.push(ptr);
} else if (s[ptr] == ')') {
if (!st.empty()) {
// 注意这里有 1 的偏移量
assign(st.top() + 1, ptr + 1, 1);
st.pop();
}
}
ptr++;
}
for (node x : odt) {
// 遍历区间
if (x.v == 0) {
for (int i = x.l; i <= x.r; i++)
putchar(s[i - 1]);
}
}
return 0;
}