JZOJ 5893] [NOIP2018模擬10.4] 括號序列 解題報告 (Hash+棧+map)
阿新 • • 發佈:2018-11-08
題目連結:
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; }