LOJ2083 优秀的拆分

Link

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;
}

posted @ 2020-07-20 23:08  Fruitea  阅读(164)  评论(0编辑  收藏  举报