题目链接:点击查看
题目大意:
给出一个只含有'(' ')' '-'三种字符长度不大于2e5个字符的字符串,并初始时有一个空串s,三种字符分别对应三种操作:
'(' :向s尾部加一个'('
')' :向s尾部加一个')'
'-' :删除s的一个元素
题目给出了平衡串的定义:
"()" 是平衡串
如果 X 是平衡串则 "(X)" 也是平衡串
如果X、Y都是平衡串则"XY"也是平衡串
问每次操作后s串中有多少个平衡串
题目分析:
因为是括号匹配问题所以容易想到开一个栈来存储左括号的下标,当遇到右括号时就可以直接取栈顶来获取与之匹配的左括号下表
每次操作都是独立的显然满足无后效性原则因此可以考虑dp
定义dp[ i ]为 s 中以第 i 个字符结尾的字符串有多少和平衡串,其前缀和即为所求答案
显然第 i 个字符是左括号时dp[ i ] = 0 ,是右括号时满足转移方程:dp[ i ] = dp[ st.top()-1 ]+1,其中st.top()即为与右括号匹配的左括号的下标
再考虑删除操作,先将答案减去dp[ pos ]来去掉最后一个字符的贡献(pos为s最后一个元素的位置),如果删除的是左括号就直接去除栈顶即可;如果删除的是右括号就需要把与之匹配的左括号入栈,实现这个过程可以使用一个数组mat,mat[ i ]表示与 i 位置的右括号相匹配的左括号的位置来方便在删除右括号时找到其位置将其入栈
具体细节见代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=200005;
stack<int>st;
ll mat[maxn],dp[maxn],pos,ans;
char s[maxn];
int main()
{
scanf("%s",s+1);
int len = strlen(s+1);
for(register int i = 1;i <= len;++i)
{
if(s[i] == '(')
{
dp[++pos] = 0;
mat[pos] = 0;
st.push(pos);
}
if(s[i] == ')')
{
if(st.empty())
{
mat[++pos] = 0;
dp[pos] = 0;
}
else {
mat[++pos] = st.top();
dp[pos] = dp[st.top()-1]+1;
st.pop();
}
ans += dp[pos];
}
if(s[i] == '-')
{
ans -= dp[pos];
if(!st.empty() && st.top()>=pos)
st.pop();
if(mat[pos]) st.push(mat[pos]);
--pos;
}
printf("%lld\n",ans);
}
return 0;
}