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

猜你喜欢

转载自www.cnblogs.com/xxzh/p/9824062.html