题目:
这个题面是真的有毒,就不发题面了。
题意是:给一串字符,求它的子序列里面有多少个满足括号序列,且相互匹配的左括号和右括号必须是同一个字符。
分析:
30分暴力:
枚举每一个子序列,扫一遍判断是否满足括号序列。
判断方法:遇到相同的就弹出,最后栈为空。
O(n^3)
60分暴力:
基于30分做法的优化:只枚举子序列的左端点x,判断的时候从x~n,只要中间某一步为空,就将答案++。
这样就可以遍历整个序列。
O(n^2)
满分做法:
基于60分做法的优化:直接扫一遍(按照进出栈的原则),求出在每一个点栈中所有元素的状态。
X表示栈为空,如下图:
把相同状态的提出来,会发现其中间夹着的区间是一段合法的括号序列。
所以只需将每一个点的状态(用hash记录)排一下序,假设有x个相同的数,ans+=(x-1)*x/2。(即这x个数两两组合得到的贡献)
为什么是对的呢?
状态相同的原因是中间经历了进栈又出栈的过程,而这个过程后,又将栈中的状态还原了,也就是说,这一段子序列是满足括号序的。
O(n)
(思想很巧妙)
#include<bits/stdc++.h> using namespace std; #define ri register int #define N 1000005 #define ll long long #define usll unsigned long long #define base 131 #define mod 998244353 char s[N],col[N];//col记录栈中元素 usll stk[N],ha[N];//记录目前栈中的hash值 记录每一个点的状态hash值 int main() { freopen("B.in","r",stdin); freopen("B.out","w",stdout); scanf("%s",s+1); int len=strlen(s+1); int top=0,cnt=0; ha[++cnt]=stk[++top]=s[1]-'a'+1; col[top]=s[1]; for(ri i=2;i<=len;++i){ if(s[i]==col[top] && top>0) top--,ha[++cnt]=stk[top]; else ha[++cnt]=stk[top]*base+s[i]-'a'+1,col[++top]=s[i],stk[top]=stk[top-1]*base+s[i]-'a'+1; } sort(ha,ha+1+cnt); ll ans=0; int now=0; while(now<=cnt){//跳连续区间统计答案 ll tmp=1; while(ha[now+tmp]==ha[now]) tmp++; tmp--; ans=(ans+tmp*(tmp+1)/2)%mod; if(tmp) now+=tmp; else now++; } printf("%lld\n",ans); } /* aababba aabaab aabbcc aabbaabbaabbaabbaabbaabb */