Codeforces 653F Paper Task : SAM

失踪人口2018ec-final出线之后。。。滚回来写的第一篇博客
cf2700-的字符串题终于全刷完了。。。这些题感觉越来越没营养。。。有一种能上红的错觉。。。

题意:

给出一个括号串,求有多少个本质不同的合法括号子串。

题解:

首先,本质不同操作,果断SAM走一波。
然后括号序列,前缀和一波,处理得到sum数组。然后 m a p &lt; i n t , v e c t o r &lt; i n t &gt; &gt; map&lt;int,vector&lt;int&gt; &gt; map<int,vector<int>>操作,sum值为S的扔到map[S]中。
然后单调栈操作,找到每个位置,左边第一个sum值比自己小的位置。处理得到Left数组。
先对于每一个节点更新出任意一个endpos,基本操作。
然后对于每个SAM节点,设为x, e n d p o s = r endpos = r endpos=r,代表串长 [ l [ f a [ x ] ] + 1 , l [ x ] ] [l[fa[x]] +1,l[x]] [l[fa[x]]+1,l[x]],则我们需要找到右端点在 r r r,且左端点在 [ m a x ( r − l [ x ] , L e f t [ r ] ) , r − l [ f a [ x ] ] ) [max(r - l[x],Left[r]),r - l[fa[x]]) [max(rl[x],Left[r]),rl[fa[x]])范围内,且左右端点的sum值相等。此即为合法括号子串的充要条件,使用upper_bound + lower_bound即可计数。

有一些细节需要仔细处理,对照第一个样例调试即可。
Code:

// Created by calabash_boy on 18-12-20.
#include<bits/stdc++.h>
using namespace std;
const int maxn = 55e4+100;
char s[maxn];int n;
inline int id(char ch){
    if (ch == '(')return 0;
    else return 1;
}
int sum[maxn];
int l[maxn];
map<int,vector<int> > pos;
struct Suffix_Automaton{
    int nxt[maxn*2][2],fa[maxn*2],l[maxn*2];
    int last,cnt;
    int endpos[maxn*2];
    int cntA[maxn*2],A[maxn*2];
    Suffix_Automaton(){ clear(); }
    void clear(){
        last =cnt=1;
        fa[1]=l[1]=0;
        memset(nxt[1],0,sizeof nxt[1]);
    }
    void init(char *s){
        while (*s){
            add(id(*s));s++;
        }
    }
    void add(int c){
        int p = last;
        int np = ++cnt;
        memset(nxt[cnt],0,sizeof nxt[cnt]);
        l[np] = l[p]+1;last = np;
        while (p&&!nxt[p][c])nxt[p][c] = np,p = fa[p];
        if (!p)fa[np]=1;
        else{
            int q = nxt[p][c];
            if (l[q]==l[p]+1)fa[np] =q;
            else{
                int nq = ++ cnt;
                l[nq] = l[p]+1;
                memcpy(nxt[nq],nxt[q],sizeof (nxt[q]));
                fa[nq] =fa[q];fa[np] = fa[q] =nq;
                while (nxt[p][c]==q)nxt[p][c] =nq,p = fa[p];
            }
        }
    }
    long long build(){
        int now = 1;
        for (int i=1;i<=n;i++){
            endpos[now = nxt[now][id(s[i])]] = i;
        }
        memset(cntA,0,sizeof cntA);
//        memset(num,0,sizeof num);
        for (int i=1;i<=cnt;i++)cntA[l[i]]++;
        for (int i=1;i<=cnt;i++)cntA[i]+=cntA[i-1];
        for (int i=cnt;i>=1;i--)A[cntA[l[i]]--] =i;
        for (int i=cnt;i>=1;i--){
            int x = A[i];
            if (!endpos[fa[x]])endpos[fa[x]] = endpos[x];
        }
        long long ans =0;
        for (int i=2;i<=cnt;i++){
            int r = endpos[i];
            int ll = ::l[r] ;
            vector<int> & V = pos[sum[r]];
         //   cerr<<"! "<<ll<<" "<<r<<"     "<<l[i]<<" "<<l[fa[i]]<<endl;
            int L = max(ll,r - l[i] );
            int R = min(r,r - (l[fa[i]]));
            if (R<L || s[r] == '(')continue;
            //for (auto x : V)cerr<<x<<" ";
            //cerr<<endl;
           // cerr<<r <<" "<<L<<","<<R<<"  "<<lower_bound(V.begin(),V.end(),R) - V.begin() <<" "<< lower_bound(V.begin(),V.end(),L) - V.begin()<<endl;
            ans += lower_bound(V.begin(),V.end(),R) - lower_bound(V.begin(),V.end(),L);
        }
        return ans;
    }
}sam;
int stk[maxn],top;
int main(){
    scanf("%d",&n);
    scanf("%s",s+1);
    sam.init(s+1);
    pos[0].push_back(0);
    for (int i=1;i<=n;i++){
        sum[i] = sum[i-1];
        if (s[i] == '(')sum[i] ++;
        else sum[i] --;
        pos[sum[i]].push_back(i);
    }
    top = 1;
    stk[0] = 0;
    sum[0] = -0x3f3f3f3f;
    for (int i=1;i<=n;i++){
        while (sum[stk[top-1]] >= sum[i])top--;
        l[i] = stk[top-1];
        stk[top++] = i;
    }
    printf("%lld\n",sam.build());
    return 0;
}
/*
0 1 0 1 0 1 0 1 0 1 0 1 0 1 0
 */
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值