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 所有子串的所有拆分中,总共有多少个是优秀的拆分。
输入输出样例
4 aabbbb cccccc aabaabaabaa bbaabaababaaba
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*2的AA串都会经过两个关键点,那么只需要枚举这些关键点就可以统计出所有答案.
我们求出i,i+len的最长公共前缀lcp和最长公共后缀lcs。lcp+lcs>len,AA串就一定存在.
然后就可以直接得出可行的开始位置的区间为:[i?lcs+1,i+lcp?len],
可行的结束位置为:[i+len?lcs+l,i+len+lcp?1].
为了避免重复统计,lcp或lcs若超过了len,那么就取len就可以了.
用差分统计一下.lcp和lcs用后缀数组+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优秀的拆分的主要内容,如果未能解决你的问题,请参考以下文章
[bzoj4650][Noi2016]优秀的拆分——后缀数组