• 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}=nln 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
    ",ans);
        }
        return 0;
    }
    
    
  • 相关阅读:
    git常用操作命令
    如何编写高质量代码
    Chrome调试工具简单介绍
    使用eclipse+tomcat搭建本地环境
    chrome设置--disable-web-security解决跨域
    利用Maven管理工程项目本地启动报错及解决方案
    用户输入验证【提升篇】
    简单【用户输入验证】
    【消息框】的返回值
    【消息框】的4种显示形式
  • 原文地址:https://www.cnblogs.com/fruitea/p/13350891.html
Copyright © 2020-2023  润新知