传送门
题意:
给一个字符串
s
s
s。
定义一个字符串
t
=
A
A
B
B
t=AABB
t=AABB是一种优秀的拆分,求
s
s
s的所有字串的优秀拆分数之和。
思路:
考虑一下
O
(
n
2
)
O(n^2)
O(n2)暴力,枚举
A
A
∣
B
B
AA|BB
AA∣BB中间的竖杠,然后枚举
A
∣
A
A|A
A∣A之间的竖杠即可。
然后思考如何优化:
考虑当你确定了B的开始位置,你所关注的仅仅是这个开始位置开始的AA形式的拆分有多少个。
那么我们可以预处理出对于所有i,从第i个位置开始的AA形式的拆分数量f[i],以及在第i-1个位置结束的AA形式的拆分的数量g[i-1]。这样我们就可以直接计算出答案了。
如何预处理???
我们枚举串
A
A
AA
AA中
A
A
A的长度
l
e
n
len
len。
然后我们找出
n
/
l
e
n
n/len
n/len个关键点:
l
e
n
,
2
∗
l
e
n
,
3
∗
l
e
n
.
.
.
len,2*len,3*len...
len,2∗len,3∗len...
这样此时满足条件的
A
A
AA
AA一定会跨过恰好两个相邻的关键点。
于是我们再枚举相邻关键点更新答案。
假设是
a
,
b
a,b
a,b。
我们发现
A
A
AA
AA如果成立,那么等价于是
A
A
A被拆分成了
S
1
,
a
−
1
S_{1,a-1}
S1,a−1的一段后缀
C
C
C和
S
b
,
n
S_{b,n}
Sb,n的一段前缀
D
D
D。
即
A
A
=
C
D
C
D
AA=CDCD
AA=CDCD。
观察发现
C
,
D
C,D
C,D的取值范围只跟
l
c
s
(
S
1
,
a
−
1
,
S
1
,
b
−
1
)
lcs(S_{1,a-1},S_{1,b-1})
lcs(S1,a−1,S1,b−1)和
l
c
p
(
S
a
,
n
,
S
b
,
n
)
lcp(S_{a,n},S_{b,n})
lcp(Sa,n,Sb,n)有关,我们用二分
+
h
a
s
h
/
S
A
+hash/SA
+hash/SA来求即可。
代码:
#include<bits/stdc++.h>
#define ri register int
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
static char buf[rlen],*ib,*ob;
(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
return ib==ob?-1:*ib++;
}
inline int read(){
int ans=0;
char ch=gc();
while(!isdigit(ch))ch=gc();
while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
return ans;
}
inline int Read(char*s){
int top=0;
char ch=gc();
while(!isalpha(ch))ch=gc();
while(isalpha(ch))s[++top]=ch,ch=gc();
return top;
}
const int N=3e4+5;
char s[N];
int n,f[N],g[N];
const int mod=1e9+9;
typedef long long ll;
typedef unsigned long long Ull;
const Ull bas=311;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
struct Hash_string{
Ull pw1[N],s1[N];
int pw2[N],s2[N];
inline void init(){
pw1[0]=pw2[0]=1;
for(ri i=1;i<=n;++i){
pw1[i]=pw1[i-1]*bas;
s1[i]=s1[i-1]*bas+(Ull)s[i];
pw2[i]=mul(pw2[i-1],bas);
s2[i]=add(mul(s2[i-1],bas),(int)s[i]);
}
}
inline Ull get1(int l,int r){return s1[r]-s1[l-1]*pw1[r-l+1];}
inline Ull get2(int l,int r){return dec(s2[r],mul(s2[l-1],pw2[r-l+1]));}
}S;
inline bool check(int l1,int r1,int l2,int r2){return S.get1(l1,r1)==S.get1(l2,r2)&&S.get2(l1,r1)==S.get2(l2,r2);}
typedef long long ll;
inline void update(int a,int b){
int l,r,mid,L,R;
if(s[a]!=s[b])R=0;
l=1,R=0,r=min(b-a,n-b+1);
while(l<=r)mid=l+r>>1,check(a,a+mid-1,b,b+mid-1)?(R=mid,l=mid+1):r=mid-1;
l=1,L=0,r=min(a,b-a-1);
while(l<=r)mid=l+r>>1,check(a-mid,a-1,b-mid,b-1)?(L=mid,l=mid+1):r=mid-1;
if(L+R<b-a)return;
++g[a-L],--g[2*a-b+R+1];
++f[2*b-a-L-1],--f[b+R];
}
int main(){
for(ri tt=read();tt;--tt){
n=Read(s);
S.init();
for(ri i=1;i<=n;++i)f[i]=g[i]=0;
for(ri len=1;len<=n;++len)for(ri a=len,b=len<<1;b<=n;a=b,b+=len)update(a,b);
ll ans=0;
for(ri i=1;i<=n;++i)f[i]+=f[i-1],g[i]+=g[i-1],ans+=(ll)f[i-1]*g[i];
cout<<ans<<'\n';
}
return 0;
}