1. 程式人生 > >JZOJ 5893] [NOIP2018模擬10.4] 括號序列 解題報告 (Hash+棧+map)

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; }