0%

正确括号序列编辑器(Correct Bracket Sequence Editor)


题目链接:洛谷 Codeforces


题目描述

最近,Polycarp开始开发一种仅适用于正确括号序列(缩写为CBS)的文本编辑器。请注意,如果可以通过添加“+”-s和“1”-s来获得正确的数学表达式,则括号序列是正确的。例如,序列“(())()”“()”和“(()(()))”是正确的,而“)(”“(()”和“(()))(”是不正确的。CBS中的每个括号都有一对。例如,在“(()(()))”中:
第1个括号与第8个括号配对,
第2个括号与第3个括号配对,
第3个括号与第2个括号配对,
第4个括号与第7个括号配对,
第5个括号与第6个括号配对,
第6个括号与第5个括号配对,
第7个括号与第4个括号配对,
第8个括号与第1个括号配对。
Polycarp的编辑器目前在使用CBS期间仅支持三种操作。编辑器中的光标占据其中一个括号的整个位置(而不是括号之间的位置!)。支持三种操作:
«L»-将光标向左移动一个位置,
«R»-将光标向右移动一个位置,
«D»-删除光标所在的括号,删除与其配对的括号以及它们之间的所有括号(即,删除光标所在的括号和与其配对的括号之间的子字符串)。
操作“D”后,光标移动到最右边的括号中(当然,在未删除的括号中)。如果没有这样的括号(即,CBS的后缀已被删除),则光标将移动到最靠近左侧的括号(当然,在未删除的括号中)。
下面的图片说明了操作“D”的几种用法。

Polycarp的编辑器不支持所有不正确的操作(将光标移到CBS的末尾,删除整个CBS等)。
Polycarp对他的开发感到非常自豪,你能实现他的编辑器的功能吗?

输入:

第一行包含三个正整数n、m和p(2≤N≤500000, 1≤M≤500000, 1≤P≤n)——正确括号顺序中括号的数量、操作的数量和光标的初始位置。序列中的位置从左到右编号,从1开始。保证n是偶数。
它后面是由n个字符组成的字符串“(“和”)”,构成正确的括号序列。
然后跟随一个由m个字符组成的字符串“L”“R”和“D”——一系列操作。从第一个到最后一个,一个接一个地进行操作。可以保证给定的操作不会将光标移到括号序列之外,并且在所有操作之后,括号序列将是非空的。

输出:

打印正确的括号顺序,这是对初始顺序应用所有操作后获得的结果。

样例输入1

8 4 5
(())()()
RDLD

样例输出1

()

样例输入2

12 5 3
((()())(()))
RRDLD

样例输出2

(()(()))

样例输入3

8 8 8
(())()()
LLLLLLDD

样例输出3

()()

提示:

在第一个样例中,光标最初位于位置5。考虑编辑的过程:命令“R”-光标移动到右侧的位置6;命令“D”-从位置5到位置6删除括号。在CBS采取形式(())()之后,光标位于位置5;命令“L”-光标移动到左侧的位置4;命令“D”-从位置1到位置4删除括号。在CBS采用()形式之后,光标位于位置1。
因此,答案等于()。

代码&注释部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,m,p;
char c;
list<char> a;
cin>>n>>m>>p;
a.push_back('!'); //在链表头设置围栏
for (int i=1;i<=n;i++) {
cin>>c;
a.push_back(c);
}
list<char>::iterator it;
list<char>::iterator posBeg,posEnd; //所要删除的起始位和终止位
list<char>::iterator temp;
it=a.begin();
it++; //起始值为原起始值后面一位
for (int i=2;i<=p;i++) it++; //后移至p位
for (int i=1;i<=m;i++) {
cin>>c;
if (c=='R') it++; //R指令右移一位
if (c=='L') it--; //L指令左移一位
if (c=='D') {
if (*it=='(') { //D指令判断是否左括号
posBeg=it; //所要删除的起始位在当前位置
posEnd=it;
posEnd++;
int cnt=1; //当前位置为(所以要向右移
for (temp=posEnd;temp!=a.end();temp++) {//从起始位后面一位开始找
if (*temp=='(') cnt++; //遇到(叠加计数器+1
if (*temp==')') cnt--; //遇到)抵消计数器-1
if (cnt==0) break; //匹配结束,退出
}
posEnd=temp; //把所要删除的终止位赋值给posEnd
posEnd++;
temp=posEnd;
if (temp==a.end()) //下一次操作后光标的重定位
it--; //如果所要删除的终止位后面是NULL,就定位到之前起始位前面一位
else it=temp; //否则就定位到所要删除的终止位后面一位
for (temp=posBeg;temp!=posEnd;) //删
a.erase(temp++);
}
else if (*it==')') { //D指令判断是否右括号
posBeg=it;
posEnd=it; //所要删除的终止位在当前位置
posBeg--;
int cnt=1; //当前位置为)所以要向左移
for (temp=posBeg;temp!=a.begin();temp--) {//从终止位前面一位开始找
if (*temp==')') cnt++; //遇到)叠加计数器+1
if (*temp=='(') cnt--; //遇到(抵消计数器-1
if (cnt==0) break; //匹配结束,退出
}
posBeg=temp; //把所要删除的起始位赋值给posBeg
posEnd++;
temp=posEnd;
if (temp==a.end()) {//下一次操作后光标的重定位
it=posBeg;
it--; //如果所要删除的终止位后面是NULL,就定位到之前起始位前面一位
}
else it++; //否则就定位到所要删除的终止位后面一位
for (temp=posBeg;temp!=posEnd;) //删
a.erase(temp++);
}
}
}
temp=a.begin();
temp++; //第一位是围栏
for (it=temp;it!=a.end();it++)
cout<<*it;
return 0;
}

花絮&起因

由于Titan让我做翻译&做测试数据
于是我生产了以下不忍直视的随机数生成程序

好玩の随机数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include<bits/stdc++.h>
using namespace std;
int main()
{
srand((int)time(0));
int n,m,p;

n=rand()%250;
n*=2;
m=rand()%100;
m*=2;
// n=rand()%250000;
// n=n*2;
// m=rand()%250000;
// m=m*2;
p=rand()%n;
cout<<n<<' '<<m<<' '<<p<<endl;
int cnt;
string s;
do {
cnt=0;
s="";
for (int i=1;i<=n;i++) {
char c;
c='('+rand()%2;
if (c=='(') cnt++;
else cnt--;
if (cnt==-1) {
cnt+=2;
c='(';
}
s=s+c;
}
}while (cnt!=0);
cout<<s<<endl;
//直到找到匹配的括号位置(懒人方法)
for (int i=1;i<=m;i++) {
int k=rand()%3;
if (k==0) cout<<"L";
else if (k==1) cout<<"R";
else cout<<"D";
}
//并没有判定迭代器是否飞出去
cout<<endl;
return 0;
}

心得&解释

  • 题目很长,翻译成中文1100字,不过废话很多。
  • 狠狠地体验了迭代器的运用。并使用了对头部和尾部的特殊操作。
  • coding完之后,过了样例以后有点开心,交上去TLE的绝望。之后想挣扎,把整个程序写了个注释,还是没有AC。之后询问了某位大佬,他告诉我是迭代器失效的问题。(虽然这个问题我在前面部分尽力规避了,但还是在最后erase的部分出了岔子。把for()中的递增语句移到循环体中,就可以防止迭代器失效的问题。)
  • 在制作数据的过程中,学到了随机数、文件读写的运用。难点是括号部分完全匹配以及题中的三个指令使得指针不越界。我在随机数生成程序中完成了用循环强制进行括号匹配,由于我源代码的特性,使得指针越界操作直接使程序无法运行,可以保证生成的指令序列合法。
谢谢你,秋刀鱼今天有吃的了!!!