NOI2016优秀的拆分

Posted 蒟蒻ZJO :-)

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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\n",ans);
131   }
132   return 0;
133 }

 

 




以上是关于NOI2016优秀的拆分的主要内容,如果未能解决你的问题,请参考以下文章

NOI2016优秀的拆分

bzoj 4650[Noi2016]优秀的拆分

bzoj 4650: [Noi2016]优秀的拆分

[bzoj4650][Noi2016]优秀的拆分——后缀数组

UOJ#219/BZOJ4650 [NOI2016]优秀的拆分 SA ST表

BZOJ 4650 [Noi2016]优秀的拆分