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

    设$f_i$表示以$i$结尾的square个数,$g_i$表示以$i$开头的square个数,则$ans=sum_{i=1}^{n-1} f_ig_{i+1}$。

    枚举square中长度的一半$L$,每$L$步取一个关键点,那么每个该长度的square的肯定恰好经过两个相邻的关键点,且位置差距为$L$的字符一一匹配。所以相邻关键点之间通过后缀数组求出最长公共前后缀,即可知道存在多少该长度的square。

    这相当于$f$和$g$的一段区间$+1$,差分前缀和后单点修改即可。

    时间复杂度$O(nlog n)$。

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #define N 60010
    using namespace std;
    char s[N];int T,n,i,j,Log[N],l,r,f[N],g[N];long long ans;
    struct DS{
      char s[N];
      int rk[N],sa[N],height[N],tmp[N],cnt[N],f[15][N];
      void suffixarray(int n,int m){
        int i,j,k;n++;
        for(i=0;i<n*2+5;i++)rk[i]=sa[i]=height[i]=tmp[i]=0;
        for(i=0;i<m;i++)cnt[i]=0;
        for(i=0;i<n;i++)cnt[rk[i]=s[i]]++;
        for(i=1;i<m;i++)cnt[i]+=cnt[i-1];
        for(i=0;i<n;i++)sa[--cnt[rk[i]]]=i;
        for(k=1;k<=n;k<<=1){
          for(i=0;i<n;i++){
            j=sa[i]-k;
            if(j<0)j+=n;
            tmp[cnt[rk[j]]++]=j;
          }
          sa[tmp[cnt[0]=0]]=j=0;
          for(i=1;i<n;i++){
            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(j=rk[height[i=k=0]=0];i<n-1;i++,k++)
          while(~k&&s[i]!=s[sa[j-1]+k])height[j]=k--,j=rk[sa[j]+1];
      }
      void build(){
        int i,j;
        for(i=1;i<=n;i++)f[0][i]=height[i];
        for(j=1;j<15;j++)for(i=1;i+(1<<j-1)<=n;i++)f[j][i]=min(f[j-1][i],f[j-1][i+(1<<j-1)]);
      }
      inline int ask(int x,int y){
        int k=Log[y-x+1];
        return min(f[k][x],f[k][y-(1<<k)+1]);
      }
      inline int lcp(int x,int y){
        x=rk[x],y=rk[y];
        if(x>y)swap(x,y);
        return ask(x+1,y);
      }
    }A,B;
    inline int lcp(int x,int y){return A.lcp(x-1,y-1);}
    inline int lcs(int x,int y){return B.lcp(n-x,n-y);}
    int main(){
      for(i=2;i<N;i++)Log[i]=Log[i>>1]+1;
      scanf("%d",&T);
      while(T--){
        scanf("%s",s);
        n=strlen(s);
        for(i=0;i<n*2+5;i++)A.s[i]=B.s[i]=0;
        for(i=0;i<n;i++)A.s[i]=B.s[n-i-1]=s[i];
        A.suffixarray(n,128);
        A.build();
        B.suffixarray(n,128);
        B.build();
        for(i=1;i<=n;i++)f[i]=g[i]=0;
        for(i=1;i+i<=n;i++)for(j=i+i;j<=n;j+=i)if(s[j-1]==s[j-i-1]){
          l=j-lcs(j,j-i)+1,r=j+lcp(j,j-i)-1;
          l=max(l+i-1,j),r=min(r,j+i-1);
          if(l<=r){
            f[l]++,f[r+1]--;
            g[l-i-i+1]++,g[r+1-i-i+1]--;
          }
        }
        for(i=1;i<=n;i++)f[i]+=f[i-1],g[i]+=g[i-1];
        for(ans=0,i=1;i<n;i++)ans+=f[i]*g[i+1];
        printf("%lld
    ",ans);
      }
      return 0;
    }
    

      

  • 相关阅读:
    android 自动化压力测试-monkey 3 命令参数
    android 自动化压力测试-monkey 2 获取程序包名
    Mysql清空表(truncate)与删除表中数据(delete)的区别
    Ubuntu下安装Navicat+mysql的各种utf-8设置
    Python——连接MongoDB
    数据库——MongoDB
    Linux——shell脚本编程2
    Linux——shell脚本编程
    centos中创建python虚拟环境(virtualenv)
    阿里云centos7下的nginx配置
  • 原文地址:https://www.cnblogs.com/clrs97/p/5731321.html
Copyright © 2011-2022 走看看