BZOJ 4650 [Noi2016]优秀的拆分 ——后缀数组
我们只需要统计在某一个点开始的形如$AA$字符串个数,和结束的个数相乘求和。
首先枚举循环节的长度L。即$\mid (A) \mid=L$
然后肯定会经过s[i]和[i+L]至少两个点。
然后我们可以枚举,然后求出循环节循环的次数、起点、终点,然后发现答案更新是一段$+1$的操作,
然后就可以用差分的思想更新即可。
#include <map> #include <cmath> #include <queue> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define F(i,j,k) for (int i=j;i<=k;++i) #define D(i,j,k) for (int i=j;i>=k;--i) #define ll long long #define mp make_pair #define maxn 70005 int _log2[maxn]; struct Suffix_Array{ int s[maxn]; int cnt[maxn],sa[maxn],rk[maxn],tmp[maxn],h[maxn]; int st[maxn][16]; void build(int n,int m) { n++; int i,j,k; F(i,0,n*2+5) sa[i]=rk[i]=tmp[i]=h[i]=0; F(i,0,m-1) cnt[i]=0; F(i,0,n-1) cnt[rk[i]=s[i]]++; F(i,1,m-1) cnt[i]+=cnt[i-1]; F(i,0,n-1) sa[--cnt[rk[i]]]=i; for (k=1;k<=n;k<<=1) { F(i,0,n-1){j=sa[i]-k;if(j<0)j+=n;tmp[cnt[rk[j]]++]=j;} sa[tmp[cnt[0]=0]]=j=0; F(i,1,n-1) { if (rk[tmp[i]]!=rk[tmp[i-1]]||rk[tmp[i]+k]!=rk[tmp[i-1]+k]) cnt[++j]=i; sa[tmp[i]]=j; } memcpy(rk,sa,n*sizeof(int));memcpy(sa,tmp,n*sizeof(int)); if (j>=n-1) break; } for (i=k=0;i<n;h[rk[i++]]=k) for (k?k--:0,j=sa[rk[i]-1];s[i+k]==s[j+k];k++); F(i,0,n-1) st[i][0]=h[i]; F(i,1,15) F(j,0,n-(1<<i)) st[j][i]=min(st[j][i-1],st[j+(1<<i-1)][i-1]); } int query(int l,int r) { int k=_log2[r-l+1]; return min(st[l][k],st[r-(1<<k)+1][k]); } int lcp(int a,int b) { int aa=rk[a],bb=rk[b]; return query(min(aa,bb)+1,max(aa,bb)); } }SA,SB; int t,n; char s[maxn]; int a[maxn],b[maxn]; int ca[maxn],cb[maxn]; void solve(int L) { for (int i=0;i+L<n;i+=L) if (s[i]==s[i+L]){ int lcp=SA.lcp(i,i+L),rcp=SB.lcp(n-i,n-i-L); if ((lcp+rcp)/L+1<2) continue; else { int xl=i-rcp,xr=i-rcp+(lcp+rcp-L);// printf("%d -- %d\n",xl,xr); int yr=i+lcp+L,yl=i+lcp+L-(lcp+rcp-L);// printf("%d -- %d\n",yl,yr); ca[xl]++;ca[xr+1]--;cb[yl]++;cb[yr+1]--; while (i<n&&i<xr) i+=L; } } } ll ans; int main() { F(i,2,maxn-1) _log2[i]=_log2[i>>1]+1; scanf("%d",&t); while (t--) { ans=0; memset(a,0,sizeof a); memset(b,0,sizeof b); memset(ca,0,sizeof ca); memset(cb,0,sizeof cb); scanf("%s",s); n=strlen(s); F(i,0,n-1) { SA.s[i]=s[i]-'a'+1; SB.s[i]=s[n-i-1]-'a'+1; } SA.s[n]=SB.s[n]=0; SA.build(n,30); SB.build(n,30); F(i,1,n) solve(i); a[0]=ca[0]; b[0]=cb[0]; F(i,1,n) a[i]=a[i-1]+ca[i],b[i]=b[i-1]+cb[i]; F(i,0,n) ans+=(ll)a[i]*b[i]; printf("%lld\n",ans); } }