题意
定义长为 \(2n\) 的字符串 \(s\) 为“双串”当且仅当 \(s_{1\cdots n}=s_{n+1\cdots 2n}\)。
定义字符串 \(s\) 的“优秀的拆分”为将该字符串分成两部分,每部分非空且都为双串。一个字符串可能有 0 种或多种优秀的拆分。
对于给定字符串 \(s\) 的所有子串求优秀的拆分数量之和。多组数据。
\(T\le 10,n\le 3\times10^4\)
分析
考虑拆贡献,设 \(a_i\) 为以 \(i\) 结尾的双串数量,\(b_i\) 为以 \(i\) 开头的双串数量,则答案就是 \(\sum_{i=1}^{n-1} a_ib_{i+1}\)。
问题在于求 \(a_i,b_i\)。考虑枚举长度 \(len\) 表示双串的长度为 \(2len\),考虑每隔 \(len\) 的长度设立一个观察点,那么一个合法双串一定会恰好覆盖两个观察点,且这两个观察点相邻。
考虑每一对相邻的观察点的贡献,那么我们只需要求出从观察点起向左最多能延伸多少,向右能延伸多少即可。这显然跟以两个观察点为起始的 lcp 和 lcs 有关。
首先,这两个东西 SA 或者二分哈希随便做。其次,不难发现若 \(lcp+lcs\le len\),则这一堆观察点没有贡献。否则,观察点向左延伸最多能延伸 \(lcs\),最少要延伸 \(len-lcp+1\),给这段区间内的 \(a\) 区间 +1,向右延伸也是同理,最少延伸 \(len-lcs+1\),最多延伸 \(lcp\)。区间加可以差分。注意 lcp 和 lcs 都要对 \(len\) 取 min。
不难发现相邻观察点数量和是调和级数,故复杂度 \(O(Tn\log n)\) 或者 \(O(Tn\log^2n)\)。后者需要卡常,但双模换单模就能过。
点击查看代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<map>
#include<unordered_map>
#include<vector>
#include<queue>
#include<stack>
#include<bitset>
#include<set>
#include<ctime>
#include<random>
#include<cassert>
#define x1 xx1
#define y1 yy1
#define IOS ios::sync_with_stdio(false)
#define ITIE cin.tie(0);
#define OTIE cout.tie(0);
#define PY puts("Yes")
#define PN puts("No")
#define PW puts("-1")
#define P0 puts("0")
#define P__ puts("")
#define PU puts("--------------------")
#define mp make_pair
#define fi first
#define se second
#define gc getchar
#define pc putchar
#define pb emplace_back
#define un using namespace
#define il inline
#define all(x) x.begin(),x.end()
#define mem(x,y) memset(x,y,sizeof x)
#define popc __builtin_popcountll
#define rep(a,b,c) for(int a=(b);a<=(c);++a)
#define per(a,b,c) for(int a=(b);a>=(c);--a)
#define reprange(a,b,c,d) for(int a=(b);a<=(c);a+=(d))
#define perrange(a,b,c,d) for(int a=(b);a>=(c);a-=(d))
#define graph(i,j,k,l) for(int i=k[j];i;i=l[i].nxt)
#define lowbit(x) ((x)&-(x))
#define lson(x) ((x)<<1)
#define rson(x) ((x)<<1|1)
//#define double long double
//#define int long long
//#define int __int128
using namespace std;
using i64=long long;
using u64=unsigned long long;
using pii=pair<int,int>;
template<typename T1,typename T2>inline void ckmx(T1 &x,T2 y){x=x>y?x:y;}
template<typename T1,typename T2>inline void ckmn(T1 &x,T2 y){x=x<y?x:y;}
inline auto rd(){int qwqx=0,qwqf=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')qwqf=-1;ch=getchar();}while(ch>='0'&&ch<='9'){qwqx=(qwqx<<1)+(qwqx<<3)+ch-48;ch=getchar();}return qwqx*qwqf;
}
template<typename T>inline void write(T qwqx,char ch='\n'){if(qwqx<0){qwqx=-qwqx;putchar('-');}int qwqy=0;char qwqz[40];while(qwqx||!qwqy){qwqz[qwqy++]=qwqx%10+48;qwqx/=10;}while(qwqy--)putchar(qwqz[qwqy]);if(ch)putchar(ch);
}
bool Mbg;
const int maxn=3e4+5,inf=0x3f3f3f3f;
const int mod=998244353,base=131;
const long long llinf=0x3f3f3f3f3f3f3f3f;
int n;
char s[maxn];
int a[maxn],b[maxn];
void upd_a(int l,int r){a[l]++,a[r+1]--;}
void upd_b(int l,int r){b[l]++,b[r+1]--;}
int f[maxn],mi[maxn];
int gash(int l,int r){return (f[r]-1ll*f[l-1]*mi[r-l+1]%mod+mod)%mod;
}
int len;
int lcp(int x,int y){int l=1,r=min(len,x),res=0;while(l<=r){int mid=(l+r)>>1;if(gash(x-mid+1,x)==gash(y-mid+1,y))res=mid,l=mid+1;else r=mid-1;;}return res;
}
int lcs(int x,int y){int l=1,r=min(len,n-y+1),res=0;while(l<=r){int mid=(l+r)>>1;if(gash(x,x+mid-1)==gash(y,y+mid-1))res=mid,l=mid+1;else r=mid-1;}return res;
}
inline void solve_the_problem(){scanf("%s",s+1),n=strlen(s+1);mi[0]=1;rep(i,1,n)mi[i]=1ll*mi[i-1]*base%mod,f[i]=(1ll*f[i-1]*base%mod+s[i])%mod;rep(i,1,n)a[i]=b[i]=0;for(len=1;len<=(n>>1);++len){rep(i,1,n/len-1){int p1=min(len,lcp(i*len,(i+1)*len)),p2=min(len,lcs(i*len,(i+1)*len));
// printf("%d %d %d %d\n",i*len,(i+1)*len,p1,p2);if(p1+p2<=len)continue;upd_b(i*len-p1+1,(i-1)*len+p2),upd_a((i+2)*len-p1,(i+1)*len+p2-1);}}rep(i,1,n)a[i]+=a[i-1],b[i]+=b[i-1];
// rep(i,1,n)write(a[i],32);P__;i64 ans=0;rep(i,1,n-1)ans+=1ll*a[i]*b[i+1];write(ans);
}
bool Med;
signed main(){
// freopen("P1117_13.in","r",stdin);freopen("out.out","w",stdout);fprintf(stderr,"%.3lfMB\n",(&Mbg-&Med)/1048576.0);int _=rd();while(_--)solve_the_problem();
}
/**/