题目描述
如果一个字符串可以被拆分为 的形式,其中 和 是任意非空字符串,则我们称该字符串的这种拆分是优秀的。
例如,对于字符串 ,如果令 , ,我们就找到了这个字符串拆分成 的一种方式。
一个字符串可能没有优秀的拆分,也可能存在不止一种优秀的拆分。比如我们令 , ,也可以用 表示出上述字符串;但是,字符串 就没有优秀的拆分。
现在给出一个长度为 的字符串 ,我们需要求出,在它所有子串的所有拆分方式中,优秀拆分的总个数。这里的子串是指字符串中连续的一段。
以下事项需要注意:
出现在不同位置的相同子串,我们认为是不同的子串,它们的优秀拆分均会被记入答案。
在一个拆分中,允许出现 。例如 存在拆分 。
字符串本身也是它的一个子串。
输入输出格式
输入格式:
每个输入文件包含多组数据。
输入的第一行只有一个整数 ,表示数据的组数。保证 。
接下来 行,每行包含一个仅由英文小写字母构成的字符串 ,意义如题所述。
输出格式:
输出
行,每行包含一个整数,表示字符串
所有子串的所有拆分中,总共有多少个是优秀的拆分。
输入输出样例
输入样例#1:
4
aabbbb
cccccc
aabaabaabaa
bbaabaababaaba
输出样例#1:
3
5
4
7
说明
我们用 表示字符串 第 个字符到第 个字符的子串(从 开始计数)。
第一组数据中,共有 个子串存在优秀的拆分:
,优秀的拆分为
,
;
,优秀的拆分为
,
;
,优秀的拆分为
,
。
而剩下的子串不存在优秀的拆分,所以第一组数据的答案是 。
第二组数据中,有两类,总共 个子串存在优秀的拆分:
对于子串 ,它们优秀的拆分相同,均为 , ,但由于这些子串位置不同,因此要计算 次;
对于子串 ,它优秀的拆分有 种: , 和 , ,它们是相同子串的不同拆分,也都要计入答案。
所以第二组数据的答案是 。
第三组数据中, 和 各有 种优秀的拆分,其中 是问题描述中的例子,所以答案是 。
第四组数据中, 各有 种优秀的拆分, 有 种优秀的拆分,所以答案是 。
对于全部的测试点,保证 。以下对数据的限制均是对于单组输入数据而言的,也就是说同一个测试点下的 组数据均满足限制条件。
我们假定
为字符串
的长度,每个测试点的详细数据范围见下表:
分析:
我们设
为以
结尾的
串个数,
为以
开头的
串个数,则
考虑怎样求 和 ,我们可以枚举 串的一半 ,然后每隔 设一个断点,那么一个长度为 的 必过两个断点。这两个断点 , 的前缀的 和这两个位置的后缀的 加起来为 ,则存在一个这样的 串;如果大于,则存在多个这样的串,而每个串可以取 和 的一部分,使得最终长度为 。这个直接后缀数组搞掉即可。
代码:
// 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);
}
}