LOJ2083 优秀的拆分
Solution
考虑优化求AA串的过程。
枚举一个长度d,设置断点(1,d+1,2d+1,...,n/d*d+1)。
长度为d的AA串一定经过其中恰好两个相邻断点。
所以可以在每对相邻断点间找所有同时经过它们的AA串,具体的:
求出lcp(i,d+i)和lcs(i,d+i),这可以通过对正串反串分别建立后缀数组得到。
如果lcp+lcs>=d,即可以占据整个中间长为d的区域(画图很好理解),那么就会有一段区间的解。
区间加就直接差分。
复杂度\(\sum_{d=1}^{\frac{n}{2}}\frac{n}{d}=n\ln n\)
got:
按长度把串分类。
必经过两个断点的性质。
lcp,lcs。
Code
#include<bits/stdc++.h>
using namespace std;
#define REP(i,a,b) for(int i=(a),_ed_=(b);i<=_ed_;++i)
#define DREP(i,a,b) for(int i=(a),_ed_=(b);i>=_ed_;--i)
#define pb push_back
#define mp make_pair
#define sz(x) (int)((x).size())
typedef long long ll;
typedef pair<int,int> pii;
inline int read(){
register int x=0,f=1;register char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=0;ch=getchar();}
while(isdigit(ch)){x=x*10+(ch^'0');ch=getchar();}
return f?x:-x;
}
char s[30005];
int n,lg[30005];
struct SuffixArray{
int sa[30005],rnk[30005],ht[30005],st[15][30005];
void SA(){
static int cnt[30005],val[60005],tmp[30005];
memset(val+1,0,sizeof(int)*(n+n)),memset(cnt+1,0,sizeof(int)*max(n,256));
REP(i,1,n)++cnt[(int)s[i]];
REP(i,1,256)cnt[i]+=cnt[i-1];
DREP(i,n,1)sa[cnt[(int)s[i]]--]=i;
REP(i,1,n){
int x=sa[i],y=sa[i-1];
rnk[x]=rnk[y];if(s[x]^s[y])++rnk[x];
}
for(int w=1;w<n;w<<=1){
int m=0;
REP(i,n-w+1,n)tmp[++m]=i;
REP(i,1,n)if(sa[i]>w)tmp[++m]=sa[i]-w;
memset(cnt+1,0,sizeof(int)*n);
REP(i,1,n)++cnt[val[i]=rnk[i]];
REP(i,1,n)cnt[i]+=cnt[i-1];
DREP(i,n,1)sa[cnt[val[tmp[i]]]--]=tmp[i];
REP(i,1,n){
int x=sa[i],y=sa[i-1];
rnk[x]=rnk[y];if(val[x]^val[y]||val[x+w]^val[y+w])++rnk[x];
}
}
for(int p=0,i=1;i<=n;++i){
p-=p>0;int j=sa[rnk[i]-1];
while(max(i,j)+p<=n&&s[i+p]==s[j+p])++p;
ht[rnk[i]]=p;
}
REP(i,1,n)st[0][i]=ht[i];
REP(j,1,lg[n])REP(i,1,n-(1<<i)+1)
st[j][i]=min(st[j-1][i],st[j-1][i+(1<<(j-1))]);
}
int query(int l,int r){
l=rnk[l],r=rnk[r];if(l>r)swap(l,r);
++l;int k=lg[r-l+1];
return min(st[k][l],st[k][r-(1<<k)+1]);
}
} A,B;
int cnt[2][30005];
int main(){
// freopen("in.in","r",stdin);
REP(i,2,30001)lg[i]=lg[i>>1]+1;
REP(T,1,read()){
scanf("%s",s+1),n=strlen(s+1);
A.SA(),reverse(s+1,s+n+1),B.SA();
memset(cnt,0,sizeof cnt);
REP(d,1,n)for(int i=d,j=d+d;j<=n;i=j,j+=d){
int lcp=A.query(i,j),lcs=B.query(n-i+1,n-j+1);
lcp=min(lcp,d),lcs=min(lcs,d);
if(lcp+lcs<=d)continue;
int x=lcp+lcs-d;
++cnt[0][i-lcs+1],--cnt[0][i-lcs+x+1];
++cnt[1][j+lcp-x],--cnt[1][j+lcp];
}
REP(i,1,n)cnt[0][i]+=cnt[0][i-1],cnt[1][i]+=cnt[1][i-1];
ll ans=0;
REP(i,1,n)ans+=1ll*cnt[0][i]*cnt[1][i-1];
printf("%lld\n",ans);
}
return 0;
}