Aizu - 1407 Parentheses Editor(对顶栈+模拟)

题目链接:点击查看

题目大意:给出一个字符串,只由 ' ( ' , ' ) ' 和 ' - ' 组成,初始时给出一个空串 s,三种字符所代表的操作如下:

  1. ' ( ' :在 s 后添加一个左括号
  2. ' ) ' :在 s 后添加一个右括号
  3. ' - ' :删除掉 s 最后的那个括号

每次删除后问有多少个合法的括号序列,合法的括号序列如下:

  1. ()
  2. X 是一个合法的括号序列,那么 ( X ) 也算
  3. 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;
}

猜你喜欢

转载自blog.csdn.net/qq_45458915/article/details/108511074