题目链接:点击查看
题目大意:给出一个字符串,只由 ' ( ' , ' ) ' 和 ' - ' 组成,初始时给出一个空串 s,三种字符所代表的操作如下:
- ' ( ' :在 s 后添加一个左括号
- ' ) ' :在 s 后添加一个右括号
- ' - ' :删除掉 s 最后的那个括号
每次删除后问有多少个合法的括号序列,合法的括号序列如下:
- ()
- X 是一个合法的括号序列,那么 ( X ) 也算
- X 和 Y 都是合法的括号序列,那么 XY 也算
题目分析:最烦写这种题目了,需要维护很多互相有关联的变量,训练的时候没插手这个题,然鹅xy哥和羊驼哥最终还是没能调出这个恶心人的题目
考虑用失配的左括号分块,记录有多少个情况三的个数,用题解的图片加以说明,比如字符串已经维护到了下面的程度:
数字代表的只是以情况 3 计数的答案,红色的左括号是失配的左括号,将整个序列分成了互不干扰的好几块,接下来考虑三种操作会造成什么影响
第一种就是在末尾加上一个左括号
当前这个新的左括号将前面的字符串隔离开来,所以只需要新加上一块,然后继续维护即可,此时新块中的计数归零
第二种是在末尾加上一个右括号
考虑两种情况,第一种情况是,这个右括号可以找到一个与之相匹配的左括号,那么在匹配之后,之前失配的左括号的这一整块都消失掉了,并且与前面的那一块进行了合并,造成的贡献就是,前面的那一块的计数加一,最终的贡献会因为两个区块的合并,加上 合并后的块的计数
再考虑另一种情况,也就是右括号无法匹配,这就说明了前面的所有左括号都得到了匹配,此时这个右括号将前后隔离开来,且此时左括号所维护的分块个数也是为 1 ,如果在后续还有操作的话,那么只需要将左括号这仅有的这个分块的计数归零即可
第三种也就是删除操作了,其实删除操作换句话说也是一种撤销操作,只需要将状态恢复到上一个状态即可,题解图示如下:
所以我们需要在合并相邻两个分块的时候额外维护一些值用于恢复,这里我选择用用了一个对顶栈来维护分块合并与撤销的信息,用了一个普通的栈维护括号序列
代码:
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=1e6+100;
stack<LL>st1,st2;//对顶栈维护分块信息
stack<char>st;//普通栈维护括号序列
char s[N];
int main()
{
#ifndef ONLINE_JUDGE
// freopen("data.in.txt","r",stdin);
// freopen("data.out.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);
scanf("%s",s+1);
int n=strlen(s+1);
LL ans=0;
st1.push(0);//初始时自带一块
for(int i=1;i<=n;i++)
{
if(s[i]=='(')//如果是左括号,额外加一个分块
{
st1.push(0);
st.push(s[i]);
}
else if(s[i]==')')//如果是右括号
{
if(st1.size()==1)//如果匹配失败
{
st2.push(st1.top());//记录一下需要撤销的值
st1.top()=0;//将st1唯一的块归零
st.push('*');//打一个特殊标记
}
else
{
st2.push(st1.top());
st1.pop();//相邻两个块的合并
st1.top()++;//倒数第二个块的总数需要加一
ans+=st1.top();//更新答案
st.push(s[i]);
}
}
else//如果是删除操作
{
if(st.top()=='(')//左括号直接删除即可
{
st1.pop();
}
else if(st.top()=='*')//如果是特殊标记的右括号,直接恢复即可,不影响答案
{
st1.top()=st2.top();
st2.pop();
}
else//如果删掉这个右括号后会影响答案,按照添加时的顺序倒序更新即可
{
ans-=st1.top();
st1.top()--;
st1.push(st2.top());
st2.pop();
}
st.pop();
}
printf("%lld\n",ans);
}
return 0;
}