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

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4650

    题意:

      给你一个字符串s,问你s及其子串中,将它们拆分成"AABB"的方式共有多少种。

    题解:

      先只考虑"AA"的形式。

      设pre[i]表示以s[i]结尾的"AA"串共有多少个,nex[i]表示以s[i]开头的"AA"串共有多少个。

      那么拆分成"AABB"的总方案数 = ∑ pre[i]*nex[i+1]。

      然后考虑如何求pre和nex数组。

      对于"AA"形式的串,有一个性质:

        假设一个"AA"串的长度为len。

        如果在s串上每隔len的距离设置一个关键点,那么这个"AA"串一定经过相邻的两个关键点。

      所以先枚举"AA"串的长度len,然后枚举是哪两个相邻的关键点。

      设当前的两个相邻关键点分别为:x = i*len, y = (i+1)*len

      设前缀s[1 to x]和前缀s[1 to y]的LCS为a,后缀s[x to n]和后缀s[y to n]的LCP为b

      令tt = a+b-1

      如果有tt >= len,则就找到了a+b-len个长度为len的"AA"串。

      又因为这些"AA"串是必须经过x,y这两个关键点的(否则会重复统计)

      所以上面的a = min(a,len), b = min(b,len)。

      找到的这些"AA"串会对pre数组和nex数组产生贡献。

      所有这些"AA"串的开头为区间[x-a+1, x+b-len],结尾为区间[y-a+len, y+b-1]。

      所以要给nex[x-a+1 to x+b-len]加1,给pre[y-a+len to y+b-1]加1

      差分一下,最后求一遍前缀个就好。

      这样pre和nex就求好了,然后统计"AABB"的总数即可。

      O(nlogn)求后缀数组

      O(nlogn)求pre和nex(调和级数复杂度)

      O(n)统计"AABB"

      总复杂度O(nlogn)

    AC Code:

      1 #include <iostream>
      2 #include <stdio.h>
      3 #include <string.h>
      4 #define MAX_N 30005
      5 #define MAX_K 20
      6 
      7 using namespace std;
      8 
      9 struct SA
     10 {
     11     int n;
     12     int a[MAX_N];
     13     int sa[MAX_N];
     14     int rk[MAX_N];
     15     int t1[MAX_N];
     16     int t2[MAX_N];
     17     int cnt[MAX_N];
     18     int tsa[MAX_N];
     19     int height[MAX_N];
     20     char s[MAX_N];
     21     
     22     int lg[MAX_N];
     23     int dat[MAX_N][MAX_K];
     24     
     25     void rsort()
     26     {
     27         memset(cnt,0,sizeof(cnt));
     28         for(int i=1;i<=n;i++) cnt[t2[i]]++;
     29         for(int i=1;i<=n;i++) cnt[i]+=cnt[i-1];
     30         for(int i=n;i>=1;i--) tsa[cnt[t2[i]]--]=i;
     31         memset(cnt,0,sizeof(cnt));
     32         for(int i=1;i<=n;i++) cnt[t1[i]]++;
     33         for(int i=1;i<=n;i++) cnt[i]+=cnt[i-1];
     34         for(int i=n;i>=1;i--) sa[cnt[t1[tsa[i]]]--]=tsa[i];
     35     }
     36     
     37     void suffix()
     38     {
     39         memset(cnt,0,sizeof(cnt));
     40         for(int i=1;i<=n;i++) a[i]=s[i],cnt[a[i]]++;
     41         for(int i='a';i<='z';i++) cnt[i]+=cnt[i-1];
     42         for(int i=1;i<=n;i++) rk[i]=cnt[a[i]];
     43         int len=1;
     44         while(len<n)
     45         {
     46             for(int i=1;i<=n;i++)
     47             {
     48                 t1[i]=rk[i];
     49                 t2[i]=i+len<=n ? rk[i+len] : 0;
     50             }
     51             rsort();
     52             for(int i=1;i<=n;i++)
     53             {
     54                 rk[sa[i]]=rk[sa[i-1]]+(t1[sa[i]]!=t1[sa[i-1]] || t2[sa[i]]!=t2[sa[i-1]]);
     55             }
     56             len<<=1;
     57         }
     58         int k=0;
     59         for(int i=1;i<=n;i++)
     60         {
     61             k=k?k-1:k;
     62             int j=sa[rk[i]-1];
     63             while(a[i+k]==a[j+k]) k++;
     64             height[rk[i]]=k;
     65         }
     66     }
     67     
     68     void init_st()
     69     {
     70         lg[0]=-1;
     71         for(int i=1;i<=n;i++)
     72         {
     73             lg[i]=lg[i>>1]+1;
     74             dat[i][0]=height[i];
     75         }
     76         for(int k=1;(1<<k)<=n;k++)
     77         {
     78             for(int i=1;i+(1<<k)-1<=n;i++)
     79             {
     80                 dat[i][k]=min(dat[i][k-1],dat[i+(1<<(k-1))][k-1]);
     81             }
     82         }
     83     }
     84     
     85     int lcp(int l,int r)
     86     {
     87         l=rk[l]; r=rk[r];
     88         if(l>r) swap(l,r); l++;
     89         int k=lg[r-l+1];
     90         return min(dat[l][k],dat[r-(1<<k)+1][k]);
     91     }
     92     
     93     void init(char *_s,int _n)
     94     {
     95         memset(a,0,sizeof(a));
     96         memset(sa,0,sizeof(sa));
     97         memset(rk,0,sizeof(rk));
     98         memset(tsa,0,sizeof(tsa));
     99         memset(height,0,sizeof(height));
    100         n=_n;
    101         for(int i=1;i<=n;i++) s[i]=_s[i];
    102         suffix();
    103         init_st();
    104     }
    105 };
    106 
    107 int n,t;
    108 char str[MAX_N];
    109 char rev[MAX_N];
    110 long long ans;
    111 long long pre[MAX_N];
    112 long long nex[MAX_N];
    113 SA sa1,sa2;
    114 
    115 inline void add(long long *a,int l,int r)
    116 {
    117     a[l]++; a[r+1]--;
    118 }
    119 
    120 void cal_aa()
    121 {
    122     memset(pre,0,sizeof(pre));
    123     memset(nex,0,sizeof(nex));
    124     for(int len=1;len<=n/2;len++)
    125     {
    126         for(int i=1;(i+1)*len<=n;i++)
    127         {
    128             int x=i*len,y=(i+1)*len;
    129             int a=sa2.lcp(n-x+1,n-y+1),b=sa1.lcp(x,y);
    130             a=min(a,len); b=min(b,len);
    131             int tt=a+b-1;
    132             if(tt>=len)
    133             {
    134                 add(nex,x-a+1,x+b-len);
    135                 add(pre,y-a+len,y+b-1);
    136             }
    137         }
    138     }
    139     for(int i=1;i<=n;i++)
    140     {
    141         pre[i]+=pre[i-1];
    142         nex[i]+=nex[i-1];
    143     }
    144 }
    145 
    146 void cal_aabb()
    147 {
    148     ans=0;
    149     for(int i=1;i<n;i++)
    150     {
    151         ans+=pre[i]*nex[i+1];
    152     }
    153 }
    154 
    155 int main()
    156 {
    157     scanf("%d",&t);
    158     while(t--)
    159     {
    160         scanf("%s",str+1);
    161         n=strlen(str+1);
    162         for(int i=1,j=n;i<=n;i++,j--) rev[j]=str[i];
    163         sa1.init(str,n); sa2.init(rev,n);
    164         cal_aa();
    165         cal_aabb();
    166         printf("%lld
    ",ans);
    167     }
    168 }
  • 相关阅读:
    JQuery 介绍
    转载:World class Software
    how to get domain user info from AD in C#
    抽象类与接口的实际应用{百分百的重点}
    设计模式学习笔记
    百度前端笔试面试7个试题
    虚函数在C#中的用法
    设计模式学习MVC实践
    VMware 创建私有网络试验室
    设计模式学习单件模式
  • 原文地址:https://www.cnblogs.com/Leohh/p/8445548.html
Copyright © 2011-2022 走看看