题目描述
如果一个字符串可以被拆分为 AABB A A B B 的形式,其中 A A 和 是任意非空字符串,则我们称该字符串的这种拆分是优秀的。
例如,对于字符串 aabaabaa a a b a a b a a ,如果令 A=aab A = a a b , B=a B = a ,我们就找到了这个字符串拆分成 AABB A A B B 的一种方式。
一个字符串可能没有优秀的拆分,也可能存在不止一种优秀的拆分。比如我们令 A=a A = a , B=baa B = b a a ,也可以用 AABB A A B B 表示出上述字符串;但是,字符串 abaabaa a b a a b a a 就没有优秀的拆分。
现在给出一个长度为 n n 的字符串 ,我们需要求出,在它所有子串的所有拆分方式中,优秀拆分的总个数。这里的子串是指字符串中连续的一段。
以下事项需要注意:
出现在不同位置的相同子串,我们认为是不同的子串,它们的优秀拆分均会被记入答案。
在一个拆分中,允许出现 A=B A = B 。例如 cccc c c c c 存在拆分 A=B=c A = B = c 。
字符串本身也是它的一个子串。
输入输出格式
输入格式:
每个输入文件包含多组数据。
输入的第一行只有一个整数 T T ,表示数据的组数。保证 。
接下来 T T 行,每行包含一个仅由英文小写字母构成的字符串 ,意义如题所述。
输出格式:
输出
T
T
行,每行包含一个整数,表示字符串 所有子串的所有拆分中,总共有多少个是优秀的拆分。
输入输出样例
输入样例#1:
4
aabbbb
cccccc
aabaabaabaa
bbaabaababaaba
输出样例#1:
3
5
4
7
说明
我们用 Si,j S i , j 表示字符串 S S 第 个字符到第 j j 个字符的子串(从 开始计数)。
第一组数据中,共有 3 3 个子串存在优秀的拆分:
,优秀的拆分为
A=a
A
=
a
,
B=b
B
=
b
;
S3,6=bbbb
S
3
,
6
=
b
b
b
b
,优秀的拆分为
A=b
A
=
b
,
B=b
B
=
b
;
S1,6=aabbbb
S
1
,
6
=
a
a
b
b
b
b
,优秀的拆分为
A=a
A
=
a
,
B=bb
B
=
b
b
。
而剩下的子串不存在优秀的拆分,所以第一组数据的答案是 3 3 。
第二组数据中,有两类,总共 个子串存在优秀的拆分:
对于子串 S1,4=S2,5=S3,6=cccc S 1 , 4 = S 2 , 5 = S 3 , 6 = c c c c ,它们优秀的拆分相同,均为 A=c A = c , B=c B = c ,但由于这些子串位置不同,因此要计算 3 3 次;
对于子串 ,它优秀的拆分有 2 2 种:, B=cc B = c c 和 A=cc A = c c , B=c B = c ,它们是相同子串的不同拆分,也都要计入答案。
所以第二组数据的答案是 3+2=5 3 + 2 = 5 。
第三组数据中, S1,8 S 1 , 8 和 S4,11 S 4 , 11 各有 2 2 种优秀的拆分,其中 是问题描述中的例子,所以答案是 2+2=4 2 + 2 = 4 。
第四组数据中, S1,4,S6,11,S7,12,S2,11,S1,8 S 1 , 4 , S 6 , 11 , S 7 , 12 , S 2 , 11 , S 1 , 8 各有 1 1 种优秀的拆分, 有 2 2 种优秀的拆分,所以答案是 。
对于全部的测试点,保证 1≤T≤10 1 ≤ T ≤ 10 。以下对数据的限制均是对于单组输入数据而言的,也就是说同一个测试点下的 T T 组数据均满足限制条件。
我们假定 为字符串
S
S
的长度,每个测试点的详细数据范围见下表:
分析:
我们设为以
i
i
结尾的串个数,
g[i]
g
[
i
]
为以
i
i
开头的串个数,则
考虑怎样求 f f 和,我们可以枚举 AA A A 串的一半 len l e n ,然后每隔 len l e n 设一个断点,那么一个长度为 2∗len 2 ∗ l e n 的 AA A A 必过两个断点。这两个断点 i i ,的前缀的 lsp l s p 和这两个位置的后缀的 lcp l c p 加起来为 len l e n ,则存在一个这样的 AA A A 串;如果大于,则存在多个这样的串,而每个串可以取 lsp l s p 和 lcp l c p 的一部分,使得最终长度为 len l e n 。这个直接后缀数组搞掉即可。
代码:
// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#define LL long long
const int maxn=2e5+7;
using namespace std;
int T,n;
int c[maxn],x[maxn],y[maxn];
LL f[maxn],g[maxn],ans;
struct suffix_array{
char s[maxn];
int rank[maxn],sa[maxn];
int h[maxn][15];
void getsa()
{
memset(rank,0,sizeof(rank));
memset(sa,0,sizeof(sa));
memset(h,0,sizeof(h));
memset(c,0,sizeof(c));
memset(x,0,sizeof(x));
memset(y,0,sizeof(y));
int m=1000;
for (int i=1;i<=m;i++) c[i]=0;
for (int i=1;i<=n;i++) x[i]=s[i];
for (int i=1;i<=n;i++) c[x[i]]++;
for (int i=1;i<=m;i++) c[i]+=c[i-1];
for (int i=n;i>0;i--) sa[c[x[i]]--]=i;
for (int k=1;k<=n;k<<=1)
{
int num=0;
for (int i=n-k+1;i<=n;i++) y[++num]=i;
for (int i=1;i<=n;i++) if (sa[i]>k) y[++num]=sa[i]-k;
for (int i=1;i<=m;i++) c[i]=0;
for (int i=1;i<=n;i++) c[x[i]]++;
for (int i=1;i<=m;i++) c[i]+=c[i-1];
for (int i=n;i>0;i--) sa[c[x[y[i]]]--]=y[i],y[i]=0;
swap(x,y);
num=1;
x[sa[1]]=1;
for (int i=2;i<=n;i++)
{
if ((y[sa[i]]!=y[sa[i-1]]) || (y[sa[i]+k]!=y[sa[i-1]+k]))
{
x[sa[i]]=++num;
}
else x[sa[i]]=num;
}
if (num>=n) break;
m=num;
}
for (int i=1;i<=n;i++) rank[i]=x[i];
}
void getheight()
{
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++;
h[rank[i]][0]=k;
}
int c=1;
for (int j=1;j<15;j++)
{
for (int i=1;i<=n;i++)
{
h[i][j]=min(h[i][j-1],h[i+c][j-1]);
}
c<<=1;
}
}
int lcp(int x,int y)
{
x=rank[x],y=rank[y];
if (x>y) swap(x,y);
x++;
int k=trunc(log(y-x+1.5)/log(2));
return min(h[x][k],h[y-(1<<k)+1][k]);
}
}A,B;
int main()
{
scanf("%d",&T);
while (T--)
{
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
scanf("%s",A.s+1);
n=strlen(A.s+1);
for (int i=1;i<=n;i++) B.s[n-i+1]=A.s[i];
A.getsa(),A.getheight();
B.getsa(),B.getheight();
for (int len=1;len<=n/2;len++)
{
for (int i=len,j=i+len;j<=n;i+=len,j+=len)
{
int x=min(A.lcp(i+1,j+1),len-1),y=min(B.lcp(n-i+1,n-j+1),len);
int t=x+y-len+1;
if (t>0)
{
g[i-y+1]++,g[i-y+1+t]--;
f[j+x+1-t]++,f[j+x+1]--;
}
}
}
for (int i=1;i<=n;i++) f[i]+=f[i-1],g[i]+=g[i-1];
ans=0;
for (int i=1;i<=n;i++) ans+=f[i]*g[i+1];
printf("%lld\n",ans);
}
}