zoukankan      html  css  js  c++  java
  • bzoj4650: [Noi2016]优秀的拆分(后缀数组+差分)

    https://www.lydsy.com/JudgeOnline/problem.php?id=4650

    如果能够预处理出

    suf[i] 以i结尾的形式为AA的子串个数

    pre[i] 以i开头的形式为AA的子串个数

    ans= ∑ suf[i]*pre[i+1]

    这两个数组的求法,类似bzoj 2119、3238

    枚举|A|的长度len,将序列每len个分一块,取每块内第一个元素作为关键点

    每个合法的AA恰好占据两个关键点

    枚举每一个关键点i,取j=i+len

    计算[i,n]和[j,n]的lcp,[1,i]和[1,j]的lcs(通过原串和反串的后缀数组)

    假设以i为基准,lcp向后匹配的最远点为r,lcs向前匹配的最远点为l

    令cnt=r-l+1 - len + 1

    那么AA的开头可以是[l,r]内任意长度为len的子串,这种子串有cnt个,即pre[l,l+cnt-1] 都会加一个贡献

    假设以j为基准,lcp向后匹配的最远点为r,lcs向前匹配的最远点为l

    令cnt=r-l+1 - len + 1

    那么AA的结尾可以是[l,r]内任意长度为len的子串,这种子串有cnt个,即pre[r,r-cnt+1] 都会加一个贡献

    利用差分累计贡献

    注意:

    用 后缀数组,有多组数据时,除了统计数量用的v数组要清零,rank数组也要清零

    后面+k 使rank 使用超过n的rank,超过n的rank存储的时上一组数据的rank

        

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    
    #define N 30002
    
    using namespace std;
    
    int n;
    char s[N];
    
    int pre[N],suf[N];
    
    int Log[N];
    
    struct SA
    {
        int a[N];
        int sa[2][N],rk[2][N];
        int v[N];
        int p,q;
        int k;
        int height[N];
        int st[N][15];
    
        void mul(int *sa,int *rk,int *SA,int *RK)
        {
            for(int i=1;i<=n;++i) v[rk[sa[i]]]=i;
            for(int i=n;i;--i) if(sa[i]>k) SA[v[rk[sa[i]-k]]--]=sa[i]-k;
            for(int i=n-k+1;i<=n;++i) SA[v[rk[i]]--]=i;
            for(int i=1;i<=n;++i) RK[SA[i]]=RK[SA[i-1]]+(rk[SA[i]]!=rk[SA[i-1]] || rk[SA[i]+k]!=rk[SA[i-1]+k]);
        }
    
        void pre_sa()
        {
            p=0; q=1;
            memset(v,0,sizeof(v));
            memset(rk,0,sizeof(rk));
            for(int i=1;i<=n;++i) v[a[i]]++;
            for(int i=1;i<=26;++i) v[i]+=v[i-1];
            for(int i=1;i<=n;++i) sa[p][v[a[i]]--]=i;
            for(int i=1;i<=n;++i) rk[p][sa[p][i]]=rk[p][sa[p][i-1]]+(a[sa[p][i]]!=a[sa[p][i-1]]);
            for(k=1;k<n;k<<=1,swap(p,q)) mul(sa[p],rk[p],sa[q],rk[q]);
        }
    
        void pre_height()
        {
            int j,k=0;
            for(int i=1;i<=n;++i)
            {
                j=sa[p][rk[p][i]-1];
                while(a[i+k]==a[j+k]) k++;
                height[rk[p][i]]=k;
                if(k) k--;
            }
        }    
    
        void pre_st()
        {
            memset(st,0,sizeof(st));
            for(int i=2;i<=n;++i) st[i][0]=height[i];
            for(int j=1,k=1;j<=14;++j,k<<=1)
                for(int i=2;i+k*2-1<=n;++i)
                    st[i][j]=min(st[i][j-1],st[i+k][j-1]);
        }
    
        void pre()
        {
            pre_sa();
            pre_height();
            pre_st();
        }
    
        int get(int i,int j)
        {
            i=rk[p][i]; j=rk[p][j];
            if(i>j) swap(i,j);
            i++;
            int l=Log[j-i+1];
            return min(st[i][l],st[j-(1<<l)+1][l]);    
        }    
    };
    SA SA1,SA2;
    
    void solve()
    {
        memset(pre,0,sizeof(pre));
        memset(suf,0,sizeof(suf));
        int j;
        int lcp,lcs;
        int cnt=0;
        for(int len=1;len<n;++len)
        {
             for(int i=len;i+len<=n;i+=len)
            {
                j=i+len;
                lcp=SA1.get(i,j);
                if(lcp>len) lcp=len;
                lcs=SA2.get(n-i+1,n-j+1);
                if(lcs>len) lcs=len;
                if(lcp+lcs-1>=len)
                {
                    suf[j+len-lcs]++;
                    suf[j+lcp]--;
                    pre[i-lcs+1]++;
                    pre[i+lcp-len+1]--;
                }
            }
        }
        for(int i=2;i<=n;++i) pre[i]+=pre[i-1],suf[i]+=suf[i-1];
        long long ans=0;
        for(int i=2;i<=n-2;++i) ans+=1LL*suf[i]*pre[i+1];
        cout<<ans<<'
    ';
    }
    
    int main()
    {
        //freopen("testdata.in","r",stdin); 
        //freopen("__.txt","w",stdout);
        int T;
        scanf("%d",&T);
        for(int i=2;i<N;++i) Log[i]=Log[i>>1]+1;
        while(T--)
        {
            scanf("%s",s+1);
            n=strlen(s+1);
            for(int i=1;i<=n;++i) SA1.a[i]=s[i]-'a'+1;
            memcpy(SA2.a,SA1.a,sizeof(SA2.a));
            reverse(SA2.a+1,SA2.a+n+1);
            SA1.a[n+1]=SA2.a[n+1]=0;
            SA1.pre();
            SA2.pre();
            solve();
        }
    }
  • 相关阅读:
    南阳oj 814 又见拦截导弹
    南阳 zb的生日和邮票分你一般(01背包思路)
    导弹拦截(最长下降子序列)变形
    控件绝对定位函数
    小玩意
    java获取本机所有可用字体
    按键监听类KeyListener及适配器改良方案
    编译php-memcached扩展
    Memcached安装,启动,连接
    Apache配置虚拟主机
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/8980353.html
Copyright © 2011-2022 走看看