zoukankan      html  css  js  c++  java
  • BZOJ 4650 [Noi2016]优秀的拆分 ——后缀数组

    我们只需要统计在某一个点开始的形如$AA$字符串个数,和结束的个数相乘求和。

    首先枚举循环节的长度L。即$mid (A) mid=L$

    然后肯定会经过s[i]和[i+L]至少两个点。

    然后我们可以枚举,然后求出循环节循环的次数、起点、终点,然后发现答案更新是一段$+1$的操作,

    然后就可以用差分的思想更新即可。

    #include <map>
    #include <cmath>
    #include <queue>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    #define F(i,j,k) for (int i=j;i<=k;++i)
    #define D(i,j,k) for (int i=j;i>=k;--i)
    #define ll long long
    #define mp make_pair
    #define maxn 70005
    int _log2[maxn];
    struct Suffix_Array{
        int s[maxn];
        int cnt[maxn],sa[maxn],rk[maxn],tmp[maxn],h[maxn];
        int st[maxn][16];
        void build(int n,int m)
        {
            n++; int i,j,k;
            F(i,0,n*2+5) sa[i]=rk[i]=tmp[i]=h[i]=0;
            F(i,0,m-1) cnt[i]=0;
            F(i,0,n-1) cnt[rk[i]=s[i]]++;
            F(i,1,m-1) cnt[i]+=cnt[i-1];
            F(i,0,n-1) sa[--cnt[rk[i]]]=i;
            for (k=1;k<=n;k<<=1)
            {
                F(i,0,n-1){j=sa[i]-k;if(j<0)j+=n;tmp[cnt[rk[j]]++]=j;}
                sa[tmp[cnt[0]=0]]=j=0;
                F(i,1,n-1)
                {
                    if (rk[tmp[i]]!=rk[tmp[i-1]]||rk[tmp[i]+k]!=rk[tmp[i-1]+k]) cnt[++j]=i;
                    sa[tmp[i]]=j;
                }
                memcpy(rk,sa,n*sizeof(int));memcpy(sa,tmp,n*sizeof(int));
                if (j>=n-1) break;
            }
            for (i=k=0;i<n;h[rk[i++]]=k)
                for (k?k--:0,j=sa[rk[i]-1];s[i+k]==s[j+k];k++);
            F(i,0,n-1) st[i][0]=h[i];
            F(i,1,15) F(j,0,n-(1<<i)) st[j][i]=min(st[j][i-1],st[j+(1<<i-1)][i-1]);
        }
        int query(int l,int r)
        {
            int k=_log2[r-l+1];
            return min(st[l][k],st[r-(1<<k)+1][k]);
        }
        int lcp(int a,int b)
        {
            int aa=rk[a],bb=rk[b];
            return query(min(aa,bb)+1,max(aa,bb));
        }
    }SA,SB;
     
    int t,n; char s[maxn];
    int a[maxn],b[maxn];
    int ca[maxn],cb[maxn];
     
    void solve(int L)
    {
        for (int i=0;i+L<n;i+=L)
        if (s[i]==s[i+L]){
            int lcp=SA.lcp(i,i+L),rcp=SB.lcp(n-i,n-i-L);
            if ((lcp+rcp)/L+1<2) continue;
            else
            {
                int xl=i-rcp,xr=i-rcp+(lcp+rcp-L);// printf("%d -- %d
    ",xl,xr);
                int yr=i+lcp+L,yl=i+lcp+L-(lcp+rcp-L);// printf("%d -- %d
    ",yl,yr);
                ca[xl]++;ca[xr+1]--;cb[yl]++;cb[yr+1]--;
                while (i<n&&i<xr) i+=L;
            }
        }
    }
     
    ll ans;
     
    int main()
    {
        F(i,2,maxn-1) _log2[i]=_log2[i>>1]+1;
        scanf("%d",&t);
        while (t--)
        {
            ans=0;
            memset(a,0,sizeof a);
            memset(b,0,sizeof b);
            memset(ca,0,sizeof ca);
            memset(cb,0,sizeof cb);
            scanf("%s",s);
            n=strlen(s);
            F(i,0,n-1)
            {
                SA.s[i]=s[i]-'a'+1;
                SB.s[i]=s[n-i-1]-'a'+1;
            }
            SA.s[n]=SB.s[n]=0;
            SA.build(n,30); SB.build(n,30);
            F(i,1,n) solve(i);
            a[0]=ca[0]; b[0]=cb[0];
            F(i,1,n) a[i]=a[i-1]+ca[i],b[i]=b[i-1]+cb[i];
            F(i,0,n) ans+=(ll)a[i]*b[i];
            printf("%lld
    ",ans);
        }
    }
    

      

  • 相关阅读:
    Hadoop添加节点的方法
    CSS中如何实现DIV居中
    Python新手入门教程,从环境准备到掌握基本编程
    八、使用pull解析器操作xml文件
    九、使用SharedPreferences进行数据存储
    十八、发送xml数据给服务器
    四、对应用进行单元测试
    二、短信发送器
    一、电话拨号器
    十四、ContentProvider往通讯录添加联系人和获取联系人
  • 原文地址:https://www.cnblogs.com/SfailSth/p/6646876.html
Copyright © 2011-2022 走看看