大致题意: 定义将一个字符串拆成(AABB)的形式为优秀拆分,求一个字符串所有子串的优秀拆分个数。
后缀数组
这题可是一道后缀数组黑题啊。
其实看完题解这题还是挺简单的。
大致思路
显然可以考虑对于每个位置(i)分别处理(AA)和(BB)的个数(pre_i)和(nxt_i),最后扫一遍相乘求和即可。
而这显然是可以用后缀数组来求的。
首先,我们枚举(i)来表示当前所找的(AA)型的字符串中(A)的长度。
然后枚举(j)和(k),其中(j=tcdot i,k=(t+1)cdot i(t)为正整数())。
接下来,我们求出(LCP(j,k))和(LCS(j-1,k-1)),分别记作(x)和(y)(注意(LCS)可以通过对一个反转后的字符串求(LCP)得到)。
如果(x+y<i),就说明当位置无法得到一个长大于等于(i)的(AABB)型字符串,可以直接跳过。
否则,就可以更新(pre)数组和(nxt)数组了。
但暴力更新依然会(TLE)。
考虑到每次更新的恰好是一段连续区间,则我们可以考虑差分。
最后扫一遍对(pre)数组和(nxt)数组求和然后相乘求解答案即可。
代码
#include<bits/stdc++.h>
#define N 30000
#define LL long long
#define min(x,y) ((x)<(y)?(x):(y))
#define swap(x,y) (x^=y^=x^=y)
using namespace std;
int n,pre[N+5],nxt[N+5];char s1[N+5],s2[N+5];
class Class_SuffixArray//后缀数组
{
private:
int n,SA[N+5],Height[N+5],rk[N+5],pos[N+5],tot[N+5];
inline void RadixSort(int S)
{
register int i;
for(i=0;i<=S;++i) tot[i]=0;
for(i=1;i<=n;++i) ++tot[rk[i]];
for(i=1;i<=S;++i) tot[i]+=tot[i-1];
for(i=n;i;--i) SA[tot[rk[pos[i]]]--]=pos[i];
}
inline void GetSA(char *s)
{
register int i,k,Size=122,cnt=0;
for(i=1;i<=n;++i) rk[pos[i]=i]=s[i-1];
for(RadixSort(Size),k=1;cnt<n;k<<=1)
{
for(Size=cnt,cnt=0,i=1;i<=k;++i) pos[++cnt]=n-k+i;
for(i=1;i<=n;++i) SA[i]>k&&(pos[++cnt]=SA[i]-k);
for(RadixSort(Size),i=1;i<=n;++i) pos[i]=rk[i];
for(rk[SA[1]]=cnt=1,i=2;i<=n;++i) rk[SA[i]]=(pos[SA[i-1]]^pos[SA[i]]||pos[SA[i-1]+k]^pos[SA[i]+k])?++cnt:cnt;
}
}
inline void GetHeight(char *s)
{
register int i,j,k=0;
for(i=1;i<=n;++i) rk[SA[i]]=i;
for(i=1;i<=n;++i)
{
if(k&&--k,!(rk[i]^1)) continue;
j=SA[rk[i]-1];
while(i+k<=n&&j+k<=n&&!(s[i+k-1]^s[j+k-1])) ++k;
Height[rk[i]]=k;
}
}
class Class_RMQ
{
private:
#define LogN 15
int Log2[N+5],Min[N+5][LogN+5];
public:
inline void Init(int len,int *data)
{
register int i,j;
for(i=2;i<=len;++i) Log2[i]=Log2[i>>1]+1;
for(i=1;i<=len;++i) Min[i][0]=data[i];
for(j=1;(1<<j-1)<=len;++j) for(i=1;i+(1<<j-1)<=len;++i) Min[i][j]=min(Min[i][j-1],Min[i+(1<<j-1)][j-1]);
}
inline int GetMin(int l,int r) {register int k=Log2[r-l+1];return min(Min[l][k],Min[r-(1<<k)+1][k]);}
}RMQ;
public:
inline void Init(int len,char *s) {n=len,GetSA(s),GetHeight(s),RMQ.Init(n,Height);}
inline int LCP(int x,int y) {return x^y?(rk[x]>rk[y]&&swap(x,y),RMQ.GetMin(rk[x]+1,rk[y])):n-x+1;}
}S1,S2;//分别存储原串和翻转后的串,分别用于求解LCP和LCS
int main()
{
register int test_tot,i,j,k,x,y,z,lst;LL ans;scanf("%d",&test_tot);
while(test_tot--)
{
for(memset(&S1,0,sizeof(S1)),memset(&S2,0,sizeof(S2)),i=0;i<=n;++i) s1[i]=s2[i]=' ',pre[i]=nxt[i]=0;//清空
for(scanf("%s",s1),n=strlen(s1),i=0;i<n;++i) s2[i]=s1[n-i-1];//读入
for(S1.Init(n,s1),S2.Init(n,s2),i=1;i<=(n>>1);++i) for(j=i,k=i<<1,lst=0;k<=n;j+=i,k+=i) j>lst&&((x=S1.LCP(j,k))+(y=S2.LCP(n-j+1,n-k+1))>i&&(++pre[k+x-1],--pre[k-y+i-1],++nxt[j-y+1],--nxt[j+x-i+1]),lst=j+x-1);//差分
for(i=n;i;--i) pre[i]+=pre[i+1];for(i=1;i<=n;++i) nxt[i]+=nxt[i-1];for(ans=0,i=1;i<n;++i) ans+=1LL*pre[i]*nxt[i+1];//统计答案
printf("%lld
",ans);
}
return 0;
}