zoukankan      html  css  js  c++  java
  • 【NOI2016】优秀的拆分

    题目描述

    如果一个字符串可以被拆分为 AABB 的形式,其中 A和 B是任意非空字符串,则我们称该字符串的这种拆分是优秀的。

    例如,对于字符串 aabaabaa,如果令 A=aab,B=a,我们就找到了这个字符串拆分成 AABB的一种方式。

    一个字符串可能没有优秀的拆分,也可能存在不止一种优秀的拆分。比如我们令 A=a,B=baa,也可以用 AABB表示出上述字符串;但是,字符串 abaabaa 就没有优秀的拆分。

    现在给出一个长度为 n的字符串 S,我们需要求出,在它所有子串的所有拆分方式中,优秀拆分的总个数。这里的子串是指字符串中连续的一段。

    以下事项需要注意:

    1.出现在不同位置的相同子串,我们认为是不同的子串,它们的优秀拆分均会被记入答案。

    2.在一个拆分中,允许出现 A=B。例如 cccc 存在拆分 A=B=c。

    3.字符串本身也是它的一个子串。

    输入输出格式

    输入格式:

    每个输入文件包含多组数据。输入文件的第一行只有一个整数 T,表示数据的组数。保证 1≤T≤10。

    接下来 T行,每行包含一个仅由英文小写字母构成的字符串 S,意义如题所述。

    输出格式:

    输出 T行,每行包含一个整数,表示字符串 S 所有子串的所有拆分中,总共有多少个是优秀的拆分。

    输入输出样例

    输入样例#1:
    4
    aabbbb
    cccccc
    aabaabaabaa
    bbaabaababaaba
    
    输出样例#1:
    3
    5
    4
    7
    

    说明

    我们用 S[i,j]表示字符串 S第 i 个字符到第 j个字符的子串(从 1开始计数)。

    第一组数据中,共有 3个子串存在优秀的拆分:

    S[1,4]=aabb,优秀的拆分为 A=a,B=b;

    S[3,6]=bbbb,优秀的拆分为 A=b,B=b;

    S[1,6]=aabbbb,优秀的拆分为 A=a,B=bb。

    而剩下的子串不存在优秀的拆分,所以第一组数据的答案是 3。

    第二组数据中,有两类,总共 4 个子串存在优秀的拆分:

    对于子串 S[1,4]=S[2,5]=S[3,6]=cccc,它们优秀的拆分相同,均为 A=c,B=c,但由于这些子串位置不同,因此要计算 3 次;

    对于子串 S[1,6]=cccccc,它优秀的拆分有 2 种:A=c,B=cc 和 A=cc,B=c,它们是相同子串的不同拆分,也都要计入答案。

    所以第二组数据的答案是 3+2=5。

    第三组数据中,S[1,8] 和 S[4,11] 各有 2 种优秀的拆分,其中 S[1,8] 是问题描述中的例子,所以答案是 2+2=4。

    第四组数据中,S[1,4],S[6,11],S[7,12],S[2,11],S[1,8] 各有 1种优秀的拆分,S[3,14] 有 2 种优秀的拆分,所以答案是 5+2=7。

    对于全部的测试点,保证 1≤T≤10。以下对数据的限制均是对于单组输入数据而言的,也就是说同一个测试点下的 T组数据均满足限制条件。

    我们假定 n 为字符串 S 的长度,每个测试点的详细数据范围见下表:

    先求出以i结尾的AA串和以i开始的AA,然后乘法原理统计下答案即可.
    关键是怎么求这两个东西.
    暴力的做法是n^2,枚举AA串的长度和AA串的位置,然后直接用lcp判断一下就可以了.
    其实很多位置是不需要枚举的,每隔len建立一个关键点,考虑到一个长度为len*2AA串都会经过两个关键点,那么只需要枚举这些关键点就可以统计出所有答案.
    我们求出i,i+len的最长公共前缀lcp和最长公共后缀lcslcp+lcs>lenAA串就一定存在.
    然后就可以直接得出可行的开始位置的区间为:[i−lcs+1,i+lcp−len]
    可行的结束位置为:[i+len−lcs+l,i+len+lcp−1].
    为了避免重复统计,lcplcs若超过了len,那么就取len就可以了.
    用差分统计一下.lcplcs用后缀数组+ST表求得.

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstdlib>
      4 #include<cstring>
      5 #include<string>
      6 #include<algorithm>
      7 #include<map>
      8 #include<complex>
      9 #include<queue>
     10 #include<stack>
     11 #include<cmath>
     12 #include<set>
     13 #include<vector>
     14 #define maxn 30010
     15 using namespace std;
     16 char s[maxn],ss[maxn];
     17 int n,k,sa[maxn],tmp[maxn],rk[maxn],rk1[maxn],lcs[maxn],lcp[maxn],f[maxn][20],g[maxn][20],sta[maxn],End[maxn];
     18 inline int Min(int i,int j){
     19   return i<j?i:j;
     20 }
     21 inline bool cmp(register int i,register int j){
     22   if(rk[i]!=rk[j]) return rk[i]<rk[j];
     23   else{
     24     register int ri=i+k<=n?rk[i+k]:-1,rj=j+k<=n?rk[j+k]:-1;
     25     return ri<rj;
     26   }
     27 }
     28 inline bool cmp1(register int i,register int j){
     29   if(rk1[i]!=rk1[j]) return rk1[i]<rk1[j];
     30   else{
     31     register int ri=i+k<=n?rk1[i+k]:-1,rj=j+k<=n?rk1[j+k]:-1;
     32     return ri<rj;
     33   }
     34 }
     35 inline void get_sa(){
     36   for(register int i=0;i<=n;i++)
     37     sa[i]=i,rk[i]=i<n?s[i]:-1;
     38   for(k=1;k<=n;k*=2){
     39     sort(sa,sa+n+1,cmp);
     40     tmp[sa[0]]=0;
     41     for(register int i=1;i<=n;i++)
     42       tmp[sa[i]]=tmp[sa[i-1]]+(cmp(sa[i-1],sa[i])?1:0);
     43     for(register int i=0;i<=n;i++)
     44       rk[i]=tmp[i];
     45   }
     46 }
     47 inline void get_sa1(){
     48   for(register int i=0;i<=n;i++)
     49     sa[i]=i,rk1[i]=i<n?ss[i]:-1;
     50   for(k=1;k<=n;k*=2){
     51     sort(sa,sa+n+1,cmp1);
     52     tmp[sa[0]]=0;
     53     for(register int i=1;i<=n;i++)
     54       tmp[sa[i]]=tmp[sa[i-1]]+(cmp1(sa[i-1],sa[i])?1:0);
     55     for(register int i=0;i<=n;i++)
     56       rk1[i]=tmp[i];
     57   }
     58 }
     59 inline void get_lcp(){
     60   for(register int i=0;i<=n;i++) rk[sa[i]]=i;
     61   register int h=0;lcp[0]=0;
     62   for(register int i=0;i<n;i++){
     63     register int j=sa[rk[i]-1];
     64     if(h>0) h--;
     65     for(;j+h<n && i+h<n;h++)
     66       if(s[j+h]!=s[i+h]) break;
     67     lcp[rk[i]-1]=h;
     68   }
     69 }
     70 inline void get_lcs(){
     71   for(register int i=0;i<=n;i++) rk1[sa[i]]=i;
     72   register int h=0;lcs[0]=0;
     73   for(register int i=0;i<n;i++){
     74     register int j=sa[rk1[i]-1];
     75     if(h>0) h--;
     76     for(;j+h<n && i+h<n;h++)
     77       if(ss[j+h]!=ss[i+h]) break;
     78     lcs[rk1[i]-1]=h;
     79   }
     80 }
     81 inline void get_st(){
     82   for(register int i=0;i<=n;i++) f[i][0]=lcp[i],g[i][0]=lcs[i];
     83   for(register int j=1;j<=18;j++)
     84     for(register int i=0;i+(1<<(j-1))<=n;i++)
     85       f[i][j]=Min(f[i][j-1],f[i+(1<<(j-1))][j-1]),
     86     g[i][j]=Min(g[i][j-1],g[i+(1<<(j-1))][j-1]);
     87 }
     88 inline int LCP(int x,int y){
     89   register int ri=Min(rk[x],rk[y]),rj=max(rk[x],rk[y]);
     90   rj--;
     91   register int ll=log(rj-ri+1)/log(2);
     92   if((1<<(ll+1))<=rj-ri+1) ll++;
     93   return Min(f[ri][ll],f[rj-(1<<ll)+1][ll]);
     94 }
     95 inline int LCS(register int x,register int y){
     96   register int ri=Min(rk1[x],rk1[y]),rj=max(rk1[x],rk1[y]);
     97   rj--;
     98   register int ll=log(rj-ri+1)/log(2);
     99   if((1<<(ll+1))<=rj-ri+1) ll++;
    100   return Min(g[ri][ll],g[rj-(1<<ll)+1][ll]);
    101 } 
    102 inline void work(){
    103   for(register int len=1;len+len<=n;len++)
    104     for(register int i=0;i+len<n;i+=len){
    105       int Lcp=Min(len,LCP(i,i+len)),Lcs=Min(len,LCS(n-i-1,n-(i+len)-1));
    106       if(Lcp+Lcs>len) sta[i-Lcs+1]++,sta[i+Lcp-len+1]--,End[i+len-Lcs+len]++,End[i+len+Lcp]--;
    107     }
    108   for(register int i=1;i<n;i++)
    109     sta[i]+=sta[i-1],End[i]+=End[i-1];
    110 }
    111 int main(){
    112   int T;
    113   scanf("%d",&T);
    114   while(T){
    115     T--;
    116     memset(sta,0,sizeof(sta));
    117     memset(End,0,sizeof(End));
    118     scanf("%s",s);
    119     n=strlen(s);
    120     for(register int i=0;i<n;i++)
    121       ss[i]=s[n-i-1];
    122     get_sa();get_lcp();
    123     get_sa1();get_lcs();
    124     get_st();
    125     work();
    126     long long ans=0;
    127     for(register int i=2;i<n-1;i++)
    128       ans+=(long long)sta[i]*End[i-1];
    129    
    130     printf("%lld
    ",ans);
    131   }
    132   return 0;
    133 }
    
    
    
     
  • 相关阅读:
    动态创建DeepZoom
    MultiSheet Excel Output
    PL/SQL Developer设置技巧
    采购审批专题总结bob
    oracle dbms包和其他包的使用大全
    ARAuto Invoice question
    应收发票相关 脚本
    发运确认停靠站错误
    让R12直接从Form登录
    查找事物处理来源
  • 原文地址:https://www.cnblogs.com/pantakill/p/7197471.html
Copyright © 2011-2022 走看看