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

    测试地址:优秀的拆分
    做法:本题需要用到后缀数组+RMQ+差分。
    容易想到,令pre(i),nxt(i)分别为以点i结尾或开头的形如AA的字符串数,那么答案就是pre(i)nxt(i+1)。那么我们怎么求这两个数组呢?
    对于一个长为L的字符串A,在它之后还有一个A的充要条件是:将字符串分成若干长度为L的段,那么字符串A必定经过或贴住一个分隔两段的点(以下简称为分割点),那么紧接在字符串A的一个长度为L的子串,也必定经过或贴住一个分割点,并且它和分割点的相对位置和字符串A相同,那么两个子串相同就等价为,对相邻的两个分割点,从两个分割点出发往左的一部分相等,往右的一部分也相等,这两个部分合起来长度为L
    转化了这个条件之后,我们又能做什么呢?注意到上面的条件仅和相邻的分割点有关,所以我们枚举L,再枚举分割点,这样枚举的时间复杂度是O(nlogn)的。接下来我们要找到从这两个分割点往左的最长相同部分,注意到这就是两个前缀的最长公共后缀,把字符串倒过来做个后缀数组,再搭配ST表做RMQ可以做到O(1)询问。向右的公共部分同理,就是求两个后缀的最长公共前缀。那么只要那个长度为L的子串在第一个分割点向左向右划出的这么一个区间内,它的后面一个长为L的子串就和它相同。注意我们每次只考虑左端点在第一个分割点之前,到它前面一个分割点为止的子串的贡献。此时为了统计贡献,我们需要一个支持区间加的结构,显然可以差分,最后再做个前缀和就能得到prenxt了。这样我们就完成了此题,时间复杂度为O(nlogn)
    (这道题好是好,就是这个数据的区分度太傻逼了,哈希暴力95,我后缀数组写挂都有65……完全没有让人写正解的诱惑力)
    以下是本人代码:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int T,n,x[30010],y[30010],s[30010],cnt[30010];
    int SA[30010],Rank[30010],height[30010];
    int rSA[30010],rrank[30010],rheight[30010];
    int len[30010],f[30010][16],rf[30010][16];
    ll pre[30010],nxt[30010];
    char S[30010],rS[30010];
    
    void init()
    {
        scanf("%s",S+1);
        S[0]='#';
        n=strlen(S)-1;
        for(int i=1;i<=n;i++)
            rS[i]=S[n-i+1];
    }
    
    void calc_SA(char *S,int *rank,int *SA)
    {
        for(int i=1;i<=n;i++)
            x[i]=S[i]-'a'+1;
        int m=26,p=1;
        while(p<n)
        {
            for(int i=1;i<=n;i++)
            {
                if (i+p<=n) y[i]=x[i+p];
                else y[i]=0;
            }
    
            memset(cnt,0,sizeof(cnt));
            for(int i=1;i<=n;i++) cnt[y[i]]++;
            for(int i=1;i<=m;i++) cnt[i]+=cnt[i-1];
            for(int i=1;i<=n;i++) s[cnt[y[i]]--]=i;
    
            memset(cnt,0,sizeof(cnt));
            for(int i=1;i<=n;i++) cnt[x[i]]++;
            for(int i=1;i<=m;i++) cnt[i]+=cnt[i-1];
            for(int i=n;i>=1;i--) SA[cnt[x[s[i]]]--]=s[i];
    
            m=0;
            for(int i=1;i<=n;i++)
            {
                if (i==1||x[SA[i]]!=x[SA[i-1]]||y[SA[i]]!=y[SA[i-1]]) m++;
                rank[SA[i]]=m;
            }
            if (m==n) break;
            for(int i=1;i<=n;i++) x[i]=rank[i];
            p<<=1;
        }
    }
    
    void calc_height(int *height,int *rank,int *SA,char *S)
    {
        int last=0;
        for(int i=1;i<=n;i++)
        {
            if (rank[i]==n)
            {
                height[rank[i]]=last=0;
                continue;
            }
            while(S[i+last]==S[SA[rank[i]+1]+last]) last++;
            height[rank[i]]=last;
            last=max(last-1,0);
        }
    }
    
    void pre_rmq()
    {
        int bit=0;
        for(int i=1;i<=n;i++)
        {
            if ((1<<(bit+1))<i) bit++;
            len[i]=bit;
        }
        for(int i=1;i<=n;i++)
            f[i][0]=height[i],rf[i][0]=rheight[i];
        for(int i=1;i<=15;i++)
            for(int j=1;j<=n-(1<<i)+1;j++)
            {
                f[j][i]=min(f[j][i-1],f[j+(1<<(i-1))][i-1]);
                rf[j][i]=min(rf[j][i-1],rf[j+(1<<(i-1))][i-1]);
            }
    }
    
    int LCP(int x,int y)
    {
        if (x<1||x>n||y<1||y>n) return 0;
        int a=Rank[x],b=Rank[y],l=min(a,b),r=max(a,b)-1,p=len[r-l+1];
        return min(f[l][p],f[r-(1<<p)+1][p]);
    }
    
    int LCS(int x,int y)
    {
        x=n-x+1,y=n-y+1;
        if (x<1||x>n||y<1||y>n) return 0;
        int a=rrank[x],b=rrank[y],l=min(a,b),r=max(a,b)-1,p=len[r-l+1];
        return min(rf[l][p],rf[r-(1<<p)+1][p]);
    }
    
    void work()
    {
        memset(pre,0,sizeof(pre));
        memset(nxt,0,sizeof(nxt));
        for(int L=1;L<=(n>>1);L++)
            for(int i=L+1;i<=n;i+=L)
            {
                int x=LCP(i,i+L),y=LCS(i-1,i+L-1);
                if (x+y>=L)
                {
                    int l=max(i-L,i-y),r=min(i-1,i+x-L);
                    if (l>r) continue;
                    nxt[l]++,nxt[r+1]--;
                    pre[l+(L<<1)-1]++,pre[r+(L<<1)]--;
                }
            }
        for(int i=1;i<=n;i++)
            pre[i]+=pre[i-1],nxt[i]+=nxt[i-1];
        ll ans=0;
        for(int i=1;i<n;i++)
            ans+=pre[i]*nxt[i+1];
        printf("%lld
    ",ans); 
    }
    
    int main()
    {
        scanf("%d",&T);
        while(T--)
        {
            init();
            calc_SA(S,Rank,SA);
            calc_height(height,Rank,SA,S);
            calc_SA(rS,rrank,rSA);
            calc_height(rheight,rrank,rSA,rS);
            pre_rmq();
            work();
        }
    
        return 0;
    }
  • 相关阅读:
    wordpress 的主题
    yapi api协作管理平台
    美团外卖券小程序路径过长导致插入文本消息失败的问题解决办法
    mp://XzDiXafjfvLnjvp
    supervisor 命令
    YII beego gin 框架对比
    芝麻微客-企业微信公域到私域流量运营助手
    H5跳转小程序
    PowerBI开发 第十九篇:基于Page创建Tooltip
    PowerBI开发 第十八篇:行级安全(RLS)
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793386.html
Copyright © 2011-2022 走看看