题目大意:给你一个字符串,求所有子串的所有优秀拆分总和,优秀的拆分被定义为一个字符串可以被拆分成4个子串,形如$AABB$,其中$AA$相同,$BB$相同,$AB$也可以相同
作为一道国赛题,95分竟然就这么给我们了!只是一个$NOIP$难度的哈希套$DP$啊......
95分就是从后往前找,统计$AA$串,每次统计一下从这个位置开始的所有子串 和 紧随其后的等长串 相同的个数$sum$
$hash(i,i+j-1)==hash(i+j,i+2*j-1) sum[i]++$
然后再统计$BB$串,就是加上在这两个串之后的位置的$sum$值,这样就统计出了合法拆分数
$dp[i]+=sum[i+2*j]$
95分就这样到手啦,竟然连自然溢出hash都不卡
1 #include <cmath> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #define ll long long 6 #define ull unsigned long long 7 #define N 2050 8 #define seed 233 9 #define idx(x) (x-'a'+1) 10 using namespace std; 11 //re 12 int T,len; 13 char str[N]; 14 int dp[N],sum[N]; 15 ull hsh[N],sp[N]; 16 ull ghsh(int x,int y) {return hsh[y]-hsh[x-1]*sp[y-x+1];} 17 void clr() 18 { 19 memset(dp,0,sizeof(dp)); 20 memset(sp,0,sizeof(sp)); 21 memset(sum,0,sizeof(sum)); 22 memset(hsh,0,sizeof(hsh)); 23 } 24 int main() 25 { 26 scanf("%d",&T); 27 while(T--) 28 { 29 clr(); 30 scanf("%s",str+1); 31 len=strlen(str+1); 32 sp[0]=1; 33 for(int i=1;i<=len;i++) 34 hsh[i]=hsh[i-1]*seed+idx(str[i]), 35 sp[i]=sp[i-1]*seed; 36 for(int i=len-1;i>=1;i--) 37 { 38 for(int j=1;i+2*j-1<=len;j++) 39 { 40 if(ghsh(i,i+j-1)==ghsh(i+j,i+2*j-1)){ 41 dp[i]++; 42 sum[i]+=dp[i+2*j]; 43 } 44 } 45 } 46 int ans=0; 47 for(int i=1;i<=len;i++) 48 ans+=sum[i]; 49 printf("%d\n",ans); 50 } 51 return 0; 52 }
接下来才是本题的重点!$NOI$岂是你想$AK$就$AK$的!出题人可能是想针对某位巨佬
不看题解这个正解思路真是很难想到......
思路大概是这样的,其实我们每次只需要统计$AA$串的数量就行了,因为$AABB$串的数量等于,在$i-1$位结束的$AA$串的数量乘以在第$i$位开始的$AA$串的数量,用公式表示就是
接下来就是统计$st$和$ed$了,感觉正解的思路很神
先用后缀数组+$ST$表处理出任意两个位置,作为后缀串开头的$LCP$以及作为前缀串末尾的$LCS$,求$LCS$可以把串反着读再去套$SA$
每次选取一个$A$串的长度$len$,再在原串每隔$len$的位置设一个关键点
然后会发现一个神奇的性质,任意一个长度为$2*len$的串必然经过且仅经过2个关键点!
接下来统计经过所有长度为$2*len$所有$AA$串,比如选定两个关键点$a,b$,且$a+len=b$
如果把$a$的和b$相同的前缀和后缀拼在一起,且拼完之后长度>=len,说明存在至少一个$AA$串
可以把$AA$串想象成一个块,在两个关键点上移动,块的左端点不能大于$a$,块的右端点不能小于$b$,块不能移动到上一个或者下一个关键点的位置,块内必须是$AA$串,这样,块所有能移动的位置-块的长度$len$,就是经过$ab$两个关键点的所有$AA$串数
时间变成,$logn$是调和级数的近似值
细节比较多需要仔细思考
1 #include <cmath> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #define ll long long 6 #define N 30100 7 #define seed 233 8 #define idx(x) (x-'a'+1) 9 using namespace std; 10 //re 11 int T,len; 12 int lg[N]; 13 ll st[N],ed[N],S[N],E[N]; 14 struct suffix{ 15 char str[N]; 16 int sa[N],tr[N],rk[N],hs[N],h[N],f[N][15],len; 17 bool check(int k,int x,int y){ 18 if(x+k>len||y+k>len) return 0; 19 else return (rk[x]==rk[y]&&rk[x+k]==rk[y+k])?1:0; 20 } 21 void get_suffix() 22 { 23 int cnt=0,i;len=strlen(str+1); 24 for(i=1;i<=len;i++) hs[str[i]]++; 25 for(i=1;i<=127;i++) if(hs[i]) tr[i]=++cnt; 26 for(i=1;i<=127;i++) hs[i]+=hs[i-1]; 27 for(i=1;i<=len;i++) rk[i]=tr[str[i]],sa[hs[str[i]]--]=i; 28 for(int k=1;cnt<len;k<<=1) 29 { 30 for(i=1;i<=cnt;i++) hs[i]=0; 31 for(i=1;i<=len;i++) hs[rk[i]]++; 32 for(i=1;i<=cnt;i++) hs[i]+=hs[i-1]; 33 for(i=len;i>=1;i--) if(sa[i]>k) tr[sa[i]-k]=hs[rk[sa[i]-k]]--; 34 for(i=1;i<=k;i++) tr[len-i+1]=hs[rk[len-i+1]]--; 35 for(i=1;i<=len;i++) sa[tr[i]]=i; 36 for(i=1,cnt=0;i<=len;i++) tr[sa[i]]=check(k,sa[i],sa[i-1])?cnt:++cnt; 37 for(i=1;i<=len;i++) rk[i]=tr[i]; 38 } 39 for(i=1;i<=len;i++) 40 { 41 if(rk[i]==1) continue; 42 for(int j=max(1,h[rk[i-1]]-1);;j++) 43 if(str[i+j-1]==str[sa[rk[i]-1]+j-1]) h[rk[i]]=j; 44 else break; 45 } 46 } 47 void get_ST() 48 { 49 for(int i=1;i<=len;i++) 50 f[i][0]=h[i]; 51 for(int k=1;(1<<k)<=len;k++) 52 for(int i=1;i+(1<<k)-1<=len;i++) 53 f[i][k]=min(f[i][k-1],f[i+(1<<(k-1))][k-1]); 54 } 55 int query(int x,int y) 56 {int L=y-x+1; 57 return min(f[x][lg[L]],f[y-(1<<lg[L])+1][lg[L]]);} 58 }p,s; 59 void clr() 60 { 61 memset(S,0,sizeof(S)),memset(st,0,sizeof(st)); 62 memset(E,0,sizeof(E)),memset(ed,0,sizeof(ed)); 63 memset(&p,0,sizeof(p)),memset(&s,0,sizeof(s)); 64 } 65 int main() 66 { 67 scanf("%d",&T); 68 for(int i=2;i<=30010;i++) 69 lg[i]=lg[i>>1]+1; 70 while(T--) 71 { 72 clr(); 73 scanf("%s",s.str+1); 74 int len=strlen(s.str+1); 75 for(int i=1;i<=len;i++) 76 p.str[i]=s.str[len-i+1]; 77 s.get_suffix(); 78 s.get_ST(); 79 p.get_suffix(); 80 p.get_ST(); 81 int sx,sy,px,py,ss,sp,l,r; 82 for(int i=1;i<=len;i++) 83 { 84 for(int j=i+1;j<=len;j+=i) 85 { 86 sx=s.rk[j-i+1],sy=s.rk[j+1]; 87 if(sx>sy) swap(sx,sy); 88 px=p.rk[len-(j-i)+1],py=p.rk[len-j+1]; 89 if(px>py) swap(px,py); 90 ss=s.query(sx+1,sy); 91 sp=p.query(px+1,py); 92 if(ss+sp<i||sp==0) continue; 93 l=max(j-i-i+1,j-i-sp+1); 94 r=min(j+i-1,j+ss); 95 S[l]++,S[r-2*i+2]--; 96 E[l+2*i-1]++,E[r+1]--; 97 } 98 } 99 for(int i=1;i<=len;i++) 100 st[i]=st[i-1]+S[i],ed[i]=ed[i-1]+E[i]; 101 ll ans=0; 102 for(int i=2;i<=len;i++) 103 ans+=ed[i-1]*st[i]; 104 printf("%lld\n",ans); 105 } 106 return 0; 107 }