[bzoj4650][Noi2016]优秀的拆分——后缀数组

Posted ylsoi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[bzoj4650][Noi2016]优秀的拆分——后缀数组相关的知识,希望对你有一定的参考价值。

题目大意:

定义一个字符串的拆分是优秀的当且仅当是(AABB)的形式,求给定字符串的所有子串的所有的拆分中有多少是优秀的。

思路:

95分太好拿了,这里直接考虑正解该怎么做。

不难发现我们只需要求出每个点开头的(AA)形式的字符串和每个点结尾的(AA)字符串,然后枚举分割点两边乘起来就好了。可是关键是(AA)形式的字符串可能有(n^2)个,直接枚举的话一定不是正解。

考虑分长度来处理所有的这种子串,对于长度为(2 imes len)(AA)形式的子串,我们将原串每隔(len)就建立一个关键点,不难发现所有长度为(2 imes len)的子串必定可以表示为两个关键点前面接一个lcs,后面接一个lcp,并且lcs和lcp的部分有重叠,重叠的部分就是可以作为两个(A)分割点的地方,得到了分割点,(AA)的起点和终点的范围也就知道了。

于是我们只需要枚举每两个关键点,然后实现区间加法,单点查询即可。

/*=======================================
 * Author : ylsoi
 * Time : 2019.2.6
 * Problem : luogu1117
 * E-mail : [email protected]
 * ====================================*/
#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define debug(x) cout<<#x<<"="<<x<<" "
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;
typedef unsigned long long ull;

using namespace std;

void File(){
    freopen("luogu1117.in","r",stdin);
    freopen("luogu1117.out","w",stdout);
}

template<typename T>void read(T &_){
    _=0; T fl=1; char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-')fl=-1;
    for(;isdigit(ch);ch=getchar())_=(_<<1)+(_<<3)+(ch^'0');
    _*=fl;
}

const int maxn=3e4+10;
int T,n,Log[maxn];

struct Suffix_Array{
    char s[maxn];
    int sz,sa[maxn],rk[maxn],tp[maxn],tax[maxn],height[maxn];
    int st[maxn][15];
    void radix_sort(){
        REP(i,1,sz)tax[i]=0;
        REP(i,1,n)++tax[rk[i]];
        REP(i,1,sz)tax[i]+=tax[i-1];
        DREP(i,n,1)sa[tax[rk[tp[i]]]--]=tp[i];
    }
    void suffix_sort(){
        memset(rk,0,sizeof(rk));
        memset(sa,0,sizeof(sa));
        memset(tp,0,sizeof(tp));
        sz=26;
        REP(i,1,n)rk[i]=s[i]-'a'+1,tp[i]=i;
        radix_sort();
        for(int w=1,p=0;w<n;w<<=1){
            p=0;
            REP(i,1,w)tp[++p]=n-w+i;
            REP(i,1,n)if(sa[i]>w)tp[++p]=sa[i]-w;
            radix_sort();
            swap(rk,tp);
            rk[sa[1]]=p=1;
            REP(i,2,n)
                if(tp[sa[i-1]]==tp[sa[i]] && tp[sa[i-1]+w]==tp[sa[i]+w])rk[sa[i]]=p;
                else rk[sa[i]]=++p;
            sz=p;
            if(sz==n)break;
        }
        int p=0;
        REP(i,1,n){
            if(p)--p;
            int j=sa[rk[i]-1];
            while(s[i+p]==s[j+p])++p;
            height[rk[i]]=p;
        }
        REP(i,1,n)st[i][0]=height[i];
        REP(j,1,Log[n])
            REP(i,1,n-(1<<j)+1)
            st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
    }
    int lcp(int x,int y){
        x=rk[x],y=rk[y];
        if(x>y)swap(x,y);
        ++x;
        int d=Log[y-x+1];
        return min(st[x][d],st[y-(1<<d)+1][d]);
    }
}S[2];

ll pre[maxn],nex[maxn];

void work(){
    memset(pre,0,sizeof(pre));
    memset(nex,0,sizeof(nex));
    REP(len,1,n/2){
        if(len==1){
            REP(i,1,n-1)if(S[0].s[i]==S[0].s[i+1]){
                ++pre[i],--pre[i+1];
                ++nex[i+1],--nex[i+2];
            }
            continue;
        }
        for(int i=len*2;i<=n;i+=len){
            int j=i-len;
            int l=max(j-S[1].lcp(n-j+1,n-i+1)+1,j-len+1);
            int r=min(j+S[0].lcp(j,i)-len,j);
            //debug(j),debug(i)<<endl;
            if(l<=r){
                ++pre[l],--pre[r+1];
                //cout<<l<<" "<<r<<endl;
                l+=len*2-1,r+=len*2-1;
                ++nex[l],--nex[r+1];
                //cout<<l<<" "<<r<<endl;
            }
        }
    }
    REP(i,1,n){
        pre[i]+=pre[i-1];
        nex[i]+=nex[i-1];
    }
    /*REP(i,1,n)printf("%lld ",pre[i]);
    printf("
");
    REP(i,1,n)printf("%lld ",nex[i]);
    printf("
");*/
    ll ans=0;
    REP(i,1,n-1)ans+=nex[i]*pre[i+1];
    printf("%lld
",ans);
}

int main(){
//  File();
    REP(i,2,3e4)Log[i]=Log[i>>1]+1;
    read(T);
    while(T--){
        scanf("%s",S[0].s+1);
        strcpy(S[1].s+1,S[0].s+1);
        n=strlen(S[0].s+1);
        reverse(S[1].s+1,S[1].s+n+1);
        S[0].suffix_sort();
        S[1].suffix_sort();
        work();
    }
    return 0;
}

以上是关于[bzoj4650][Noi2016]优秀的拆分——后缀数组的主要内容,如果未能解决你的问题,请参考以下文章

UOJ#219/BZOJ4650 [NOI2016]优秀的拆分 SA ST表

BZOJ 4650 [Noi2016]优秀的拆分:后缀数组

[bzoj4650][Noi2016]优秀的拆分——后缀数组

BZOJ 4650 [Noi2016]优秀的拆分

bzoj4650: [Noi2016]优秀的拆分

BZOJ4650&UOJ219优秀的拆分(二分,hash)