• UOJ#219/BZOJ4650 [NOI2016]优秀的拆分 字符串 SA ST表


    原文链接http://www.cnblogs.com/zhouzhendong/p/9025092.html

    题目传送门 - UOJ#219 (推荐,题面清晰)

    题目传送门 - BZOJ4650

    题意

      如果一个字符串可以被拆分为AABB的形式,其中AA和BB是任意非空字符串,则我们称该字符串的这种拆分是优秀的。

      现在给出一个长度为n的字符串S,我们需要求出,在它所有子串的所有拆分方式中,优秀拆分的总个数。这里的子串是指字符串中连续的一段。

      多组数据,数据组数$leq 10,nleq 30000$

    题解

      考虑先处理$AA$类型的字符串,对于每一个位置$i$,分别处理以$i$位置开头或结尾的$AA$类型字符串个数。

      如果完成了这个处理,那么最后的统计便毫不费力。(注意开longlong)

      考虑如何处理。

      首先,枚举$A$串的长度,记为$L$。我们设置一些关键点,依次为$1L,2L,3L,cdots,iL,(i+1)L,cdots$。

      考虑处理相邻两个关键点对答案的贡献。对于第$i$对相邻的关键点,我们需要得到一个特殊的最长公共子串。

      这个最长公共子串由结尾为$iL,(i+1)L$的长度不大于$L$的最长公共后缀(LCS) 和 开头为$iL,(i+1)L$的长度不大于$L$的最长公共前缀(LCP) 拼接而成。

      于是你可以通过删除一些这个最长公共子串的后缀和前缀,使得包含两个关键点的相同串首尾相接,来得到长度为$2L$的$AA$类型字符串。

      这样的串对于一开始要处理的那个东西有一个区间的贡献。可以用差分来搞定。

      至于求$LCS$和$LCP$,我们只需要先后缀数组+ST表预处理一下,就可以了。

      时间复杂度$O(nlog n)$。

    代码

    #include <bits/stdc++.h>
    #define rank r_a_n_k
    using namespace std;
    typedef long long LL;
    const int N=60005;
    int T,n,m,tmp[N],SA[N],rank[N],tax[N],height[N];
    int ST[N][18],A[N],B[N];
    char s[N];
    void Sort(int n){
    	for (int i=0;i<=m;i++)
    		tax[i]=0;
    	for (int i=1;i<=n;i++)
    		tax[rank[i]]++;
    	for (int i=1;i<=m;i++)
    		tax[i]+=tax[i-1];
    	for (int i=n;i>=1;i--)
    		SA[tax[rank[tmp[i]]]--]=tmp[i];
    }
    bool cmp(int rk[],int x,int y,int w){
    	return rk[x]==rk[y]&&rk[x+w]==rk[y+w];
    }
    void Suffix_Array(char s[],int n){
    	memset(SA,0,sizeof SA);
    	memset(tmp,0,sizeof tmp);
    	memset(rank,0,sizeof rank);
    	memset(height,0,sizeof height);
    	for (int i=1;i<=n;i++)
    		rank[i]=s[i],tmp[i]=i;
    	m=127;
    	Sort(n);
    	for (int w=1,p=0;p<n;w<<=1,m=p){
    		p=0;
    		for (int i=n-w+1;i<=n;i++)
    			tmp[++p]=i;
    		for (int i=1;i<=n;i++)
    			if (SA[i]>w)
    				tmp[++p]=SA[i]-w;
    		Sort(n);
    		swap(tmp,rank);
    		rank[SA[1]]=p=1;
    		for (int i=2;i<=n;i++)
    			rank[SA[i]]=cmp(tmp,SA[i],SA[i-1],w)?p:++p;
    	}
    	for (int i=1,j,k=0;i<=n;height[rank[i++]]=k)
    		for (k=max(k-1,0),j=SA[rank[i]-1];s[i+k]==s[j+k];k++);
    }
    void GetST(int n){
    	memset(ST,0,sizeof ST);
    	for (int i=1;i<=n;i++){
    		ST[i][0]=height[i];
    		for (int j=1;j<18;j++){
    			ST[i][j]=ST[i][j-1];
    			if (i-(1<<(j-1))>0)
    				ST[i][j]=min(ST[i][j],ST[i-(1<<(j-1))][j-1]);
    		}
    	}
    }
    int QueryST(int L,int R){
    	int val=floor(log(R-L+1)/log(2));
    	return min(ST[L+(1<<val)-1][val],ST[R][val]);
    }
    int LCP(int i,int j){
    	i=rank[i],j=rank[j];
    	return QueryST(min(i,j)+1,max(i,j));
    }
    int LCS(int i,int j){
    	return LCP(n*2-i+2,n*2-j+2);
    }
    int main(){
    	scanf("%d",&T);
    	while (T--){
    		memset(s,0,sizeof s);
    		scanf("%s",s+1);
    		n=strlen(s+1);
    		s[n+1]='#';
    		for (int i=n;i>=1;i--)
    			s[n*2-i+2]=s[i];
    		Suffix_Array(s,n*2+1);
    		GetST(n*2+1);
    		memset(A,0,sizeof A);
    		memset(B,0,sizeof B);
    		for (int L=1;L<=n;L++)
    			for (int i=1;i<=n/L-1;i++){
    				int lcp=min(LCP(L*i,L*(i+1)),L);
    				int lcs=min(LCS(L*i,L*(i+1)),L);
    				if (lcp+lcs<=L)
    					continue;
    				int l=i*L-lcs+1,r=(i+1)*L+lcp-1;
    				int cov=lcp+lcs-(L+1);
    				B[l]++,B[l+cov+1]--;
    				A[r-cov]++,A[r+1]--;
    			}
    		for (int i=1;i<=n;i++)
    			A[i]+=A[i-1],B[i]+=B[i-1];
    		LL ans=0;
    		for (int i=1;i<=n;i++)
    			ans+=1LL*A[i]*B[i+1];
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    

      

      

  • 相关阅读:
    北科的秋天
    最大子段和问题(dp)
    cmd应用
    问题 H: 抽奖活动(大数)
    大数算法
    模板整理(三)
    在CMD中建立一个不能删除的文件
    波利亚(Polya)罐子模型
    51nod-迷宫问题(Dijkstra算法)
    优先队列
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/BZOJ2650.html
Copyright © 2020-2023  润新知