如果一个字符串可以被拆分为 AABBAABB 的形式,其中 AA 和 BB 是任意非空字符串,则我们称该字符串的这种拆分是优秀的。
例如,对于字符串 aabaabaa,如果令 A=aab,B=a,我们就找到了这个字符串拆分成 AABB 的一种方式。
一个字符串可能没有优秀的拆分,也可能存在不止一种优秀的拆分。
比如我们令 A=a,B=baa,也可以用 AABB 表示出上述字符串;但是,字符串 abaabaa 就没有优秀的拆分。
现在给出一个长度为 n 的字符串 S,我们需要求出,在它所有子串的所有拆分方式中,优秀拆分的总个数。
这里的子串是指字符串中连续的一段。
以下事项需要注意:出现在不同位置的相同子串,我们认为是不同的子串,它们的优秀拆分均会被记入答案。
在一个拆分中,允许出现 A=B。例如 cccc 存在拆分 A=B=c。字符串本身也是它的一个子串。
每个输入文件包含多组数据。
输入文件的第一行只有一个整数 T,表示数据的组数。
保证 1≤T≤10。
接下来 T 行,每行包含一个仅由英文小写字母构成的字符串 S,意义如题所述。
输出 T 行,每行包含一个整数,表示字符串 S 所有子串的所有拆分中,总共有多少个是优秀的拆分。
显然我们不用处理什么$AABB$,只需要去处理所有$AA$的形式,再去统计答案即可。
设$Left[i]$表示以$i$这个字符开头的$AA$型子串的数目。
设$Right[i]$表示以$i$这个字符结尾的$AA$型子串的数目。
则:$Ans=\sum_{i=1}^{n-1}Left[i+1]\times Right[i]$
顺序求一遍,反过来再求一遍,$Left[i],Right[i]$就可以求解了。
问题是怎么求解。
我们可以枚举找的$AA$型子串长度的一半,去判断$LCP$与$LCS$
枚举$i=k\times len,j=i+len,j<=n$
设$x=LCP(suffix(i),suffix(j)),y=LCS(prefix(i−1),prefix(j−1))$
若$x+y>=len$,那么我们就找到了$x+y−len+1$个长度为$2\times len$的$AA$型子串
差分一下,最后做一遍前缀和即可。
注:记得清空数组。。。
附代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#define MAXN 50010
using namespace std;
int n;
long long Left[MAXN],Right[MAXN];
inline int read(){
int date=0,w=1;char c=0;
while(c<‘0‘||c>‘9‘){if(c==‘-‘)w=-1;c=getchar();}
while(c>=‘0‘&&c<=‘9‘){date=date*10+c-‘0‘;c=getchar();}
return date*w;
}
struct Suffix_Array{
char str[MAXN];
int top,sa[MAXN],rk[MAXN],tax[MAXN],tp[MAXN],height[MAXN],f[MAXN][20];
void radixsort(){
for(int i=0;i<=top;i++)tax[i]=0;
for(int i=1;i<=n;i++)tax[rk[i]]++;
for(int i=1;i<=top;i++)tax[i]+=tax[i-1];
for(int i=n;i>=1;i--)sa[tax[rk[tp[i]]]--]=tp[i];
}
void suffixsort(){
top=128;
for(int i=1;i<=n;i++){
rk[i]=str[i];
tp[i]=i;
}
radixsort();
for(int w=1,p=0;p<n;top=p,w<<=1){
p=0;
for(int i=1;i<=w;i++)tp[++p]=n-w+i;
for(int i=1;i<=n;i++)if(sa[i]>w)tp[++p]=sa[i]-w;
radixsort();
swap(tp,rk);
rk[sa[1]]=p=1;
for(int i=2;i<=n;i++)
rk[sa[i]]=(tp[sa[i-1]]==tp[sa[i]]&&tp[sa[i-1]+w]==tp[sa[i]+w])?p:++p;
}
}
void getheight(){
for(int i=1,j,k=0;i<=n;i++){
if(k)k--;
j=sa[rk[i]-1];
while(str[i+k]==str[j+k])k++;
height[rk[i]]=k;
}
}
void step(){
for(int i=1;i<=n;i++)f[i][0]=height[i];
for(int i=1;(1<<i)<=n;i++)
for(int j=1;j+(1<<i)-1<=n;j++)
f[j][i]=min(f[j][i-1],f[j+(1<<(i-1))][i-1]);
}
int query(int l,int r){
l=rk[l];r=rk[r];
if(l>r)swap(l,r);
l++;
int k=(int)log2(r-l+1);
return min(f[l][k],f[r-(1<<k)+1][k]);
}
inline void clean(){
memset(str,0,sizeof(str));
memset(sa,0,sizeof(sa));
memset(rk,0,sizeof(rk));
memset(height,0,sizeof(height));
memset(tp,0,sizeof(tp));
memset(f,0,sizeof(f));
}
inline void build(){
suffixsort();
getheight();
step();
}
}A,B;
void work(){
long long ans=0;
for(int k=1;k<=(n>>1);k++)
for(int i=k,j=i+k;j<=n;i+=k,j+=k){
int x=min(k,A.query(i,j)),y=min(k-1,B.query(n-(i-1)+1,n-(j-1)+1));
if(x+y>=k){
int t=x+y-k+1;
Left[i-y]++;Left[i-y+t]--;
Right[j+x-t]++;Right[j+x]--;
}
}
for(int i=1;i<=n;i++){
Left[i]+=Left[i-1];
Right[i]+=Right[i-1];
}
for(int i=1;i<=n;i++)ans+=Left[i+1]*Right[i];
printf("%lld\n",ans);
}
void init(){
memset(Left,0,sizeof(Left));
memset(Right,0,sizeof(Right));
A.clean();B.clean();
scanf("%s",A.str+1);
n=strlen(A.str+1);
for(int i=1;i<=n;i++)B.str[i]=A.str[n-i+1];
A.build();B.build();
}
int main(){
int t=read();
while(t--){
init();
work();
}
return 0;
}