题意
如果一个字符串可以被拆分为 AABB 的形式,其中 A 和 B 是任意非空字符串,则我们称该字符串的这种拆分是优秀的。例如,对于字符串 aabaabaa,如果令 A=aab,B=a,我们就找到了这个字符串拆分成 AABB 的一种方式。一个字符串可能没有优秀的拆分,也可能存在不止一种优秀的拆分。比如我们令 A=a,B=baa,也可以用 AABB 表示出上述字符串;但是,字符串 abaabaa 就没有优秀的拆分。现在给出一个长度为 n 的字符串 S,我们需要求出,在它所有子串的所有拆分方式中,优秀拆分的总个数。这里的子串是指字符串中连续的一段。以下事项需要注意:出现在不同位置的相同子串,我们认为是不同的子串,它们的优秀拆分均会被记入答案。在一个拆分中,允许出现 A=B。例如 cccc 存在拆分 A=B=c。字符串本身也是它的一个子串。
T<=10,n<=30000
分析
这个做法还是比较巧妙的。
设
f[i]
表示有多少个重复串以
i
为开头,
那么答案就是
考虑如何求 f[i] 和 g[i] 。
枚举重复串一半的长度
L
,在串上每
然后我们就可以用
sa
来求
lcp
,这样的话就相当于是给
f
和
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
const int N=30005;
int n,bin[20],lg[N],b[N],c[N],d[N],f[N*2],g[N*2];
char str[N];
struct Suffix_Array
{
int rank[N*2],sa[N],height[N],rmq[N][17],s[N];
void get_sa()
{
for (int i=0;i<=30;i++) b[i]=0;
for (int i=1;i<=n;i++) b[s[i]]++;
for (int i=1;i<=30;i++) b[i]+=b[i-1];
for (int i=n;i>=1;i--) c[b[s[i]]--]=i;
int t=0,j=1;c[0]=0;
for (int i=1;i<=n;i++)
{
if (s[c[i]]!=s[c[i-1]]) t++;
rank[c[i]]=t;
}
while (j<=n)
{
for (int i=1;i<=n;i++) b[i]=0;
for (int i=1;i<=n;i++) b[rank[i+j]]++;
for (int i=1;i<=n;i++) b[i]+=b[i-1];
for (int i=n;i>=1;i--) c[b[rank[i+j]]--]=i;
for (int i=1;i<=n;i++) b[i]=0;
for (int i=1;i<=n;i++) b[rank[i]]++;
for (int i=1;i<=n;i++) b[i]+=b[i-1];
for (int i=n;i>=1;i--) d[b[rank[c[i]]]--]=c[i];
t=0;
for (int i=1;i<=n;i++)
{
if (rank[d[i]]!=rank[d[i-1]]||rank[d[i]]==rank[d[i-1]]&&rank[d[i]+j]!=rank[d[i-1]+j]) t++;
c[d[i]]=t;
}
for (int i=1;i<=n;i++) rank[i]=c[i];
if (t==n) break;
j<<=1;
}
for (int i=1;i<=n;i++) sa[rank[i]]=i;
}
void get_height()
{
int k=0;
for (int i=1;i<=n;i++)
{
if (k) k--;
int j=sa[rank[i]-1];
while (i+k<=n&&j+k<=n&&s[i+k]==s[j+k]) k++;
height[rank[i]]=k;
}
}
void build()
{
memset(rank,0,sizeof(rank));
get_sa();
get_height();
for (int i=1;i<=n;i++) rmq[i][0]=height[i];
for (int j=1;j<=lg[n];j++)
for (int i=1;i+bin[j]-1<=n;i++)
rmq[i][j]=min(rmq[i][j-1],rmq[i+bin[j-1]][j-1]);
}
int get_lcp(int x,int y)
{
x=rank[x];y=rank[y];
if (x>y) swap(x,y);
x++;int l=lg[y-x+1];
return min(rmq[x][l],rmq[y-bin[l]+1][l]);
}
}sa1,sa2;
int main()
{
bin[0]=1;
for (int i=1;i<=15;i++) bin[i]=bin[i-1]*2;
for (int i=1;i<=30000;i++) lg[i]=log(i)/log(2);
int T;scanf("%d",&T);
while (T--)
{
scanf("%s",str+1);n=strlen(str+1);
for (int i=1;i<=n;i++) sa1.s[i]=str[i]-'a'+1;
sa1.build();
for (int i=1;i<=n;i++) sa2.s[i]=str[n-i+1]-'a'+1;
sa2.build();
for (int i=1;i<=n;i++) f[i]=g[i]=0;
for (int L=1;L<=n;L++)
for (int i=1;i+L<=n;i+=L)
{
int j=i+L,s1=min(sa1.get_lcp(i,j),L),s2=min(sa2.get_lcp(n-i+1,n-j+1),L);
int l=i-s2+1,r=i+s1-1;
if (r-l+1<L) continue;
f[l]++;f[r-L+2]--;
g[l+L*2-1]++;g[r+L+1]--;
}
for (int i=1;i<=n;i++) f[i]+=f[i-1],g[i]+=g[i-1];
LL ans=0;
for (int i=1;i<n;i++) ans+=(LL)g[i]*f[i+1];
printf("%lld\n",ans);
}
return 0;
}