zoukankan      html  css  js  c++  java
  • 【BZOJ4650】[NOI2016] 优秀的拆分(后缀数组)

    点此看题面

    大致题意: 定义将一个字符串拆成(AABB)的形式为优秀拆分,求一个字符串所有子串的优秀拆分个数。

    后缀数组

    这题可是一道后缀数组黑题啊。

    其实看完题解这题还是挺简单的。

    大致思路

    显然可以考虑对于每个位置(i)分别处理(AA)(BB)的个数(pre_i)(nxt_i),最后扫一遍相乘求和即可。

    而这显然是可以用后缀数组来求的。

    首先,我们枚举(i)来表示当前所找的(AA)型的字符串中(A)的长度。

    然后枚举(j)(k),其中(j=tcdot i,k=(t+1)cdot i(t)为正整数())

    接下来,我们求出(LCP(j,k))(LCS(j-1,k-1)),分别记作(x)(y)(注意(LCS)可以通过对一个反转后的字符串求(LCP)得到)。

    如果(x+y<i),就说明当位置无法得到一个长大于等于(i)(AABB)型字符串,可以直接跳过。

    否则,就可以更新(pre)数组和(nxt)数组了。

    但暴力更新依然会(TLE)

    考虑到每次更新的恰好是一段连续区间,则我们可以考虑差分

    最后扫一遍对(pre)数组和(nxt)数组求和然后相乘求解答案即可。

    代码

    #include<bits/stdc++.h>
    #define N 30000
    #define LL long long
    #define min(x,y) ((x)<(y)?(x):(y))
    #define swap(x,y) (x^=y^=x^=y)
    using namespace std;
    int n,pre[N+5],nxt[N+5];char s1[N+5],s2[N+5];
    class Class_SuffixArray//后缀数组
    {
        private:
            int n,SA[N+5],Height[N+5],rk[N+5],pos[N+5],tot[N+5];
            inline void RadixSort(int S)
            {
                register int i;
                for(i=0;i<=S;++i) tot[i]=0;
                for(i=1;i<=n;++i) ++tot[rk[i]];
                for(i=1;i<=S;++i) tot[i]+=tot[i-1];
                for(i=n;i;--i) SA[tot[rk[pos[i]]]--]=pos[i];
            }
            inline void GetSA(char *s)
            {
                register int i,k,Size=122,cnt=0;
                for(i=1;i<=n;++i) rk[pos[i]=i]=s[i-1];
                for(RadixSort(Size),k=1;cnt<n;k<<=1)
                {
                    for(Size=cnt,cnt=0,i=1;i<=k;++i) pos[++cnt]=n-k+i;
                    for(i=1;i<=n;++i) SA[i]>k&&(pos[++cnt]=SA[i]-k);
                    for(RadixSort(Size),i=1;i<=n;++i) pos[i]=rk[i];
                    for(rk[SA[1]]=cnt=1,i=2;i<=n;++i) rk[SA[i]]=(pos[SA[i-1]]^pos[SA[i]]||pos[SA[i-1]+k]^pos[SA[i]+k])?++cnt:cnt;
                }
            }
            inline void GetHeight(char *s)
            {
                register int i,j,k=0;
                for(i=1;i<=n;++i) rk[SA[i]]=i;
                for(i=1;i<=n;++i)
                {
                    if(k&&--k,!(rk[i]^1)) continue;
                    j=SA[rk[i]-1];
                    while(i+k<=n&&j+k<=n&&!(s[i+k-1]^s[j+k-1])) ++k;
                    Height[rk[i]]=k;
                }
            }
            class Class_RMQ
            {
                private:
                    #define LogN 15
                    int Log2[N+5],Min[N+5][LogN+5];
                public:
                    inline void Init(int len,int *data)
                    {
                        register int i,j;
                        for(i=2;i<=len;++i) Log2[i]=Log2[i>>1]+1;
                        for(i=1;i<=len;++i) Min[i][0]=data[i];
                        for(j=1;(1<<j-1)<=len;++j) for(i=1;i+(1<<j-1)<=len;++i) Min[i][j]=min(Min[i][j-1],Min[i+(1<<j-1)][j-1]);
                    }
                    inline int GetMin(int l,int r) {register int k=Log2[r-l+1];return min(Min[l][k],Min[r-(1<<k)+1][k]);}
            }RMQ;
        public:
            inline void Init(int len,char *s) {n=len,GetSA(s),GetHeight(s),RMQ.Init(n,Height);}
            inline int LCP(int x,int y) {return x^y?(rk[x]>rk[y]&&swap(x,y),RMQ.GetMin(rk[x]+1,rk[y])):n-x+1;}
    }S1,S2;//分别存储原串和翻转后的串,分别用于求解LCP和LCS
    int main()
    {
        register int test_tot,i,j,k,x,y,z,lst;LL ans;scanf("%d",&test_tot);
        while(test_tot--)
        {
            for(memset(&S1,0,sizeof(S1)),memset(&S2,0,sizeof(S2)),i=0;i<=n;++i) s1[i]=s2[i]='',pre[i]=nxt[i]=0;//清空
            for(scanf("%s",s1),n=strlen(s1),i=0;i<n;++i) s2[i]=s1[n-i-1];//读入
            for(S1.Init(n,s1),S2.Init(n,s2),i=1;i<=(n>>1);++i) for(j=i,k=i<<1,lst=0;k<=n;j+=i,k+=i) j>lst&&((x=S1.LCP(j,k))+(y=S2.LCP(n-j+1,n-k+1))>i&&(++pre[k+x-1],--pre[k-y+i-1],++nxt[j-y+1],--nxt[j+x-i+1]),lst=j+x-1);//差分
            for(i=n;i;--i) pre[i]+=pre[i+1];for(i=1;i<=n;++i) nxt[i]+=nxt[i-1];for(ans=0,i=1;i<n;++i) ans+=1LL*pre[i]*nxt[i+1];//统计答案
            printf("%lld
    ",ans);
        }
        return 0;
    }
    
  • 相关阅读:
    vim字体设置
    windows下eclipse打不开
    ubuntu的无线网无法连上
    将ubuntu系统录到u盘上
    使用UltraISO刻录系统到U盘可能会出现打不开的情况
    windows安装程序制作
    安装ubuntu双系统
    数据库插入数据时间比较
    LeetCode 189. Rotate Array
    LeetCode 228. Summary Ranges
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ4650.html
Copyright © 2011-2022 走看看