故事开端
忆昔当年泪不干,在这么一个平凡的夜晚,我在牛客打了一发小白月赛。当时的我并不知道这场比赛将会对现在的我造成多大的影响,以至于如今我时常怀念那个无所不能的自己。**
(好像也不是很厉害)

可以看到,七道题目我过了五题,水平还算好吧,看看最后两题过的人这么少(自豪)
好了也不多bb,开始进入正题
初次相遇

我当时一看到这题,脑子里面浮现好几种做法,其中就有栈(有关栈的描述请看oi wiki 栈),但是最后我选择用数组来做,我也不知道我怎么想的,可能是因为以前在洛谷做过类似的题,比如P1739 表达式括号匹配(用栈来验证左右括号是否完全匹配),就想用没做过的办法来写,于是就用了字符数组来做。
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; #define ll long long #define NO cout<<"NO"<<endl #define YES cout<<"YES"<<endl char a[2000500]; int main(){ ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); int n,k,num1,num2; cin>>n>>k; for(int i=1;i<=n;i++){ cin>>a[i]; if(a[i]=='I') num1=i-1,num2=i+1; } for(int i=1;i<=k;i++){ string b; cin>>b; if(b=="backspace"){ if(a[num1]=='('){ if(a[num2]==')'){ num1--; num2++; } else num1--; } else if(a[num1]==')') num1--; } else{ if(a[num2]=='('||a[num2]==')') num2++; } } for(int i=1;i<=num1;i++) cout<<a[i]; cout<<'I'; for(int i=num2;i<=n;i++) cout<<a[i]; return 0; }
|
这题也是十分简单的一题,不过字符串的题总是特别会给自己加戏,比如这里一个特判那里一个特判,我就是因为忘记判断delete删除时光标右边是否存在括号直接wa2发,罚时上天,哭了OVO
本来做到这里我就想润了,因为我其实是一边打游戏一边比赛的,而且这题是第三题,已经浪费了我许多宝贵的游戏时间了,但是挑战自我的想法猛地从我内心窜出来,我决定再写十分钟(其实是不想被别人比下去)
灵感大爆发!

一刻也没有为上一题哀悼,随着赶到战场的是——hard 删除括号!
这题相对于上题,难度就在于这个新增的操作:光标左右移。我当时觉得如果仍然采用上题的思路来写,会导致一个很棘手的问题:(以光标左边为例)光标左移后,原先的一个括号移动到光标右侧,那变动的这个括号该如何处理?(好像这玩意也可以用a[num2-1]来处理,然后num2- -,相当于变动的括号移到右边,不过我当时没想到这个,这个对不对我也没检验过)
然后我就想啊想,想了一会就想下播了,已经不想打力。然后我都打开游戏界面了,但突然想起来栈这玩意,我当时就在想,判断一序列括号是否合法不就可以用栈进出来做吗,脑海里就浮现出一张图,光标左移就是把左边的栈的栈顶取出,栈内括号数量减一,那这个出栈的括号又没有删除,怎么处理呢?那不就是放到右边的栈吗?!(此时把题目输入的字符串分为两个栈,一个栈存光标左边的括号,另一个存右边的括号,两个栈一个进栈序列是顺序,一个是逆序,就相当于两个栈的出口相对,也就是对顶栈)
来不及解释了,我一边思考着这个问题,一边敲代码测试,代码如下:
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
| #include"bits/stdc++.h" using namespace std; #define ll long long #define NO cout<<"NO"<<endl #define YES cout<<"YES"<<endl int main(){ ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); int n,k,flag=0,num1,num2,num; string c=""; cin>>n>>k; stack<char> ds1,ds2; for(int i=1;i<=n;i++){ char a; cin>>a; if(a=='I') flag=1; if(flag==0) ds1.push(a); if(flag==1&&a!='I') c+=a; } for(int i=c.length()-1;i>=0;i--) ds2.push(c[i]); for(int i=1;i<=k;i++){ string b; cin>>b; if(b=="backspace"){ char tmp1='1',tmp2='1'; if(!ds1.empty()) tmp1=ds1.top(),ds1.pop(); if(!ds2.empty()) tmp2=ds2.top(); if(tmp1=='('&&tmp2==')') ds2.pop(); } else if(b=="delete"){ if(!ds2.empty()) ds2.pop(); } else if(b=="<-"){ if(!ds1.empty()){ char tmp1=ds1.top(); ds1.pop(); ds2.push(tmp1); } } else{ if(!ds2.empty()){ char tmp1=ds2.top(); ds2.pop(); ds1.push(tmp1); } } } string a="",b=""; while(!ds1.empty()){ char tmp=ds1.top(); a+=tmp; ds1.pop(); } for(int i=a.length()-1;i>=0;i--) cout<<a[i]; cout<<'I'; while(!ds2.empty()){ char tmp=ds2.top(); cout<<tmp; ds2.pop(); } return 0; }
|
第一次交的时候wa了,当时我心脏都停跳了,这么完美的想法居然是错的?本着写都写了的理念,我开始找样例调试,然后发现:我当时ds2也用了一个字符串存出栈序列来逆序输出,这就导致了光标右边的括号都是反的,而题目给的样例都是要么括号删完了,要么光标移动到最右边了,所以能过样例。改完之后我忐忑地点了提交,过了几秒就出现那个死牛和死人音效,悬着的心终于放下来了O.o

尾声
总的来说,这两道字符串其实不是很难,只要你能想到怎么处理那就很简单了,但是赛场上能及时想出正确的解法也是一件不容易的事,事后我去看了讲解,才知道我写的这玩意叫对顶栈,很简单的栈的运用,但是是我在比赛中灵感一现写出来的,对我来说也有很大的纪念意义,写这篇文章也算是回顾比赛加深知识理解了,以后我还会记录更多的奇思妙想,加纳。