zoukankan      html  css  js  c++  java
  • [Noi2016]优秀的拆分

    来自F allDream的博客,未经允许,请勿转载,谢谢。


    如果一个字符串可以被拆分为 AABB 的形式,其中 A和 B是任意非空字符串,则我们称该字符串的这种拆分是优秀的。

    例如,对于字符串 aabaabaa,如果令 A=aab,B=a,我们就找到了这个字符串拆分成 AABB的一种方式。

    一个字符串可能没有优秀的拆分,也可能存在不止一种优秀的拆分。比如我们令 A=a,B=baa,也可以用 AABB表示出上述字符串;但是,字符串 abaabaa 就没有优秀的拆分。

    现在给出一个长度为 n的字符串 S,我们需要求出,在它所有子串的所有拆分方式中,优秀拆分的总个数。这里的子串是指字符串中连续的一段。

    以下事项需要注意:

    1.出现在不同位置的相同子串,我们认为是不同的子串,它们的优秀拆分均会被记入答案。

    2.在一个拆分中,允许出现 A=B。例如 cccc 存在拆分 A=B=c。

    3.字符串本身也是它的一个子串

    你需要计算一个字符串的所有子串的优秀的拆分的个数之和

    T(T<=10)组数据,n<=30000

    这道题n^2暴力95分..

    正解的话反正我是想不到...

    如果知道以每个点为左/右端点构成AA的方案数,就可以计算答案

    考虑求出正着反着各求出后缀数组之后,枚举长度L,然后每段长度是L把这个字符串分段

    每次枚举两个端点i,j(i和j都是L的倍数,j=i+L),并且计算左端点在[i-L+1,i]内的答案。

    所以求一下点i,j正着的lcp,反着的lcp,也就是从点i开始能向左右扩展到哪里,如果能扩展到的长度大等于L,那么就有答案

    假设倒着时候的lcp是a,正着的是b,也就是[i-a+1,i]和[j-a+1,j]相同(显然a>0),b同理,那么有答案的左端点区间是[i-a+1,i-a+1 + (a+b-1-L) -1] 差分即可。

    复杂度Tnlogn

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define ll long long
    #define MN 30000
    #define MD 15
    using namespace std;
    inline int read()
    {
        int x = 0; char ch = getchar();
        while(ch < '0' || ch > '9') ch = getchar();
        while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
        return x;
    }
    
    int n,k,lt[MN+5],rt[MN+5],v[MN+5],Log[MN+5];ll ans;
    char st[MN*2+5],st2[MN*2+5];
    
    void CalSa(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 GetH(int*H,int*sa,int*rk,char*s)
    {
        for(int i=1,k=0;i<=n;H[rk[i++]-1]=k,k?--k:0) if(rk[i]>1)
            for(int j=sa[rk[i]-1];s[j+k]==s[i+k];++k);
        
    }
    
    struct SuffixArray
    {
        int p,q,sa[2][MN+5],rk[2][MN+5],f[MD+1][MN+5],F[MD+1][MN+5],fa[MD+1][MN+5],Fa[MD+1][MN+5];
        inline void Clear(){p=1;q=0;
            memset(rk,0,sizeof(rk));
            memset(sa,0,sizeof(sa));
        }
        void Build(char *s)
        {
            memset(v,0,sizeof(int)*30);
            for(int i=1;i<=n;++i)++v[s[i]-'a'];
            for(int i=1;i<26;++i) v[i]+=v[i-1];
            for(int i=1;i<=n;++i) sa[q][v[s[i]-'a']--]=i;
            for(int i=1;i<=n;++i)
                rk[q][sa[q][i]]=rk[q][sa[q][i-1]]+(s[sa[q][i]]!=s[sa[q][i-1]]);
            for(k=1;k<n;k<<=1)
            {
                CalSa(sa[p],rk[p],sa[q],rk[q]);
                swap(p,q);
            }
        }
        void BuildHeight(char *s)
        {
            GetH(f[0],sa[q],rk[q],s);
            for(int i=2;i<=n;++i) F[0][i]=f[0][i-1];
            for(int i=0;i<=MD;++i) F[i][0]=f[i][0]=f[i][n]=F[i][1]=MN;
            for(int i=1;i<n;++i) fa[0][i]=i+1,Fa[0][i+1]=i;
            for(int i=1;i<=MD;++i)
                for(int j=1;j<=n;++j)
                    fa[i][j]=fa[i-1][fa[i-1][j]],
                    Fa[i][j]=Fa[i-1][Fa[i-1][j]],
                     f[i][j]=min(f[i-1][j],f[i-1][fa[i-1][j]]),
                     F[i][j]=min(F[i-1][j],F[i-1][Fa[i-1][j]]);
        }
        int query(int i,int j)
        {
            i=rk[q][i],j=rk[q][j];
            if(i>j) swap(i,j);
            int L=Log[j-i];
            return min(f[L][i],F[L][j]);
        }
    }P,S;
    
    int main()
    {
        Log[0]=-1;
        for(int i=1;i<=MN;++i) Log[i]=Log[i>>1]+1;
        for(int T=read();T;--T)
        {
            scanf("%s",st+1);n=strlen(st+1);
            for(int i=1;i<=n;++i) st2[i]=st[n-i+1];
            P.Clear();S.Clear();ans=0;
            P.Build(st);S.Build(st2);
            P.BuildHeight(st);
            S.BuildHeight(st2);
            memset(lt,0,sizeof(lt));
            memset(rt,0,sizeof(rt));
            for(int L=1;L<=n;++L)
                for(int i=L,j=i+L;j<=n;i=j,j+=L)
                {
                    int a=P.query(i,j),b=S.query(n-j+1,n-i+1);
                    if(min(a,L)+min(b,L)-1>=L)
                    {
                        int len=min(a,L)+min(b,L)-1-L;
                        ++lt[i-min(b,L)+1],--lt[i-min(b,L)+len+2],
                        ++rt[j-min(b,L)+L],--rt[j-min(b,L)+L+len+1];
                    }
                }
            for(int i=1;i<=n;++i) lt[i]+=lt[i-1],rt[i]+=rt[i-1];
            for(int i=1;i<n;++i) ans+=1LL*rt[i]*lt[i+1];
            printf("%lld
    ",ans);
        }
        return 0;
    }
  • 相关阅读:
    贝叶斯公式的直观理解(先验概率/后验概率)
    linux生成.so库如何指定添加其他的.so库
    第一章笔记
    高学成
    第一张笔记
    简单编译器之语法分析
    浅析Netty的异步事件驱动(二)
    浅析Netty的异步事件驱动(一)
    简单编译器之词法分析
    Android TV 焦点控制逻辑
  • 原文地址:https://www.cnblogs.com/FallDream/p/Noi2016d1t1.html
Copyright © 2011-2022 走看看