这题的$O(n^2)$暴力可以拿到$95$分,用哈希预处理出$f_i$表示以$i$结尾的AA串数量,再用哈希和$f_i$统计答案即可
然后考虑正解,设$f_i$表示以$i$开头的AA串数量,$g_i$表示以$i$结尾的AA串数量,那么答案就是$\sum\limits_{i=1}^{n-1}g_if_{i+1}$,现在的问题就是预处理$f$和$g$
枚举AA串中A的长度$L$,把整个字符串中的$i,i+L,\cdots$称为“关键点”,那么一个AA串必定跨越两个关键点
对于每两个相邻的关键点$u,v(v=u+L)$,我们求出$x=\text{lcp}(S_{u\cdots n},S_{v\cdots n}),y=\text{lcs}(S_{1\cdots u},S_{1\cdots v})$(这里的lcs是最长公共 后缀),那么$S_{u-y+1\cdots u+x-1}=S_{v-y+1\cdots v+x-1}$,这时如果$x+y-1\geq L$,说明我们找到了一些合法的AA,它可以起始于$[u-y+1,u+x-L]$并终止于$[v+x-L,v+x-1]$,区间$+1$直接差分即可(注意同样长度的AA不能在同一个位置出现多次,特判一下即可)
为了偷懒,对字符串的处理我用了二分+哈希,总时间复杂度$O(n\log_2^2n)$,可是这毒瘤数据居然卡自然溢出哈希...不过大概也是我的姿势水平不太够
#include<stdio.h> #include<string.h> typedef long long ll; const int mod=998244353; int min(int a,int b){return a<b?a:b;} int max(int a,int b){return a>b?a:b;} char s[30010]; ll h[30010],bs[30010]; int f[30010],g[30010],n; int get(int l,int r){return((h[r]-h[l-1]*bs[r-l+1])%mod+mod)%mod;} int lcp(int x,int y){ int l,r,mid,ans; l=1; r=min(n-x+1,n-y+1); ans=0; while(l<=r){ mid=(l+r)>>1; if(get(x,x+mid-1)==get(y,y+mid-1)){ ans=mid; l=mid+1; }else r=mid-1; } return ans; } int lcs(int x,int y){ int l,r,mid,ans; l=1; r=min(x,y); ans=0; while(l<=r){ mid=(l+r)>>1; if(get(x-mid+1,x)==get(y-mid+1,y)){ ans=mid; l=mid+1; }else r=mid-1; } return ans; } void add(int*a,int l,int r){ if(l>r)return; a[l]++; a[r+1]--; } ll work(){ int L,i,a,b,enf,eng; ll ans; scanf("%s",s+1); n=strlen(s+1); bs[0]=1; for(i=1;i<=n;i++){ h[i]=(h[i-1]*379ll+s[i]-'a')%mod; bs[i]=bs[i-1]*379ll%mod; } memset(f,0,sizeof(f)); memset(g,0,sizeof(g)); for(L=1;L<=n>>1;L++){ enf=eng=0; for(i=1;i+L<=n;i+=L){ a=lcp(i,i+L); b=lcs(i,i+L); if(a+b-1>=L){ add(f,max(i-b+1,enf+1),i+a-L); enf=max(enf,i+a-L); add(g,max(i+L-b+L,eng+1),i+L+a-1); eng=max(eng,i+L+a-1); } } } for(i=1;i<=n;i++){ f[i]+=f[i-1]; g[i]+=g[i-1]; } ans=0; for(i=1;i<n;i++)ans+=(ll)g[i]*f[i+1]; return ans; } int main(){ int T; scanf("%d",&T); while(T--)printf("%lld\n",work()); }