[JZOJ 5893] [NOIP2018模拟10.4] 括号序列 解题报告 (Hash+栈+map)
题目链接:
https://jzoj.net/senior/#main/show/5893
题目:
题解:
考虑暴力怎么做,我们枚举左端点,维护一个栈,依次加入元素,与栈顶元素和栈内第二个元素相同时弹出栈顶和第二个元素。若某个时刻栈为空则说明当前区间是合法的,累加答案。
为什么相同就直接弹出呢?会不会当前的括号与之后的括号匹配呢?仔细想想发现二者都是合法的,不需要多次计算答案
现在优化这个暴力,从1开始,对于两个位置$i,j(i<j)$,若栈内的元素相同,那么说明$[i+1,j]$这一段是一个合法的区间。于是我们考虑对于每个位置的栈内元素hash,用map$存储当前hash值出现的次数,每次累加答案就是了
#include<algorithm> #include<cstring> #include<cstdio> #include<iostream> #include<map> using namespace std; typedef long long ll; typedef unsigned long long ull; const int N=1e6+15; const ll P=19260817; ll tp,n,ans,now; ll sta[N]; ull pin[N]; char s[N]; map<ull,ll> Hash; int main(){ freopen("bracket.in","r",stdin); freopen("bracket.out","w",stdout); scanf("%s",s+1); n=strlen(s+1); pin[0]=1; for (int i=1;i<=n;i++) pin[i]=pin[i-1]*P; Hash[0]++; for (int i=1;i<=n;i++){ ll x=s[i]-'a'+1;//注意这里需要+1 sta[++tp]=x; now=now+pin[tp]*sta[tp]; if (tp>=2&&sta[tp]==sta[tp-1]){ now=now-pin[tp]*sta[tp]-pin[tp-1]*sta[tp-1]; tp-=2; } ans+=Hash[now]; Hash[now]++; } printf("%lld\n",ans); return 0; }
星星之火,终将成燎原之势