• [LOJ 2083][UOJ 219][BZOJ 4650][NOI 2016]优秀的拆分


    [LOJ 2083][UOJ 219][BZOJ 4650][NOI 2016]优秀的拆分

    题意

    给定一个字符串 (S), 求有多少种将 (S) 的子串拆分为形如 AABB 的拆分方案

    (|S|le 30000) ((95\%) 数据 (|S|le 2000))

    题解

    考场上遇见这题直接打95分暴力哈希跑路就完事了吧

    (O(n^2)) 暴力就直接枚举所有子串看它是不是 AA 型的, 在左右端点处分别标记一下, 然后枚举断点把两边的方案数乘起来就完事了.

    考虑优化这个暴力. 我们枚举这个 AA 串中 A 的长度 (l), 然后每隔 (l) 取一个关键点, 那么每个 AA 串必然会覆盖两个关键点. 对于每对相邻的关键点 (a)(b), 我们计算 (p=operatorname{LCP}(S[a:],S[b:])) 以及 (s=operatorname{LCS}(S[:a-1],S[:b-1])) . 那么只要 (p+sge l) 就会有 AA 串出现. 画画图可以发现 ([a-s,a-(l-p)]) 都可以是 AA 串的左端点. 直接在差分数组上修改左右端点就可以了. 注意只能计算同时包含这两个相邻的关键点的 AA 串, 所以应该和 ([a-l+1,a]) 取交集. 算完之后前缀和一下按照 (O(n^2)) 暴力里的操作算最终答案就可以了.

    参考代码

    #include <bits/stdc++.h>
    
    const int MAXN=1e5+10;
    typedef long long intEx;
    
    struct SuffixArray{
    	char s[MAXN];
    	int SA[MAXN];
    	int rank[MAXN];
    	int stmin[20][MAXN];
    	int* height=stmin[0];
    	void Build();
    	int LCP(int,int);
    };
    SuffixArray pf,sf;
    
    int n;
    int lg[MAXN];
    int cnt[MAXN];
    char buf[MAXN];
    int lcnt[MAXN];
    int rcnt[MAXN];
    int* x=new int[MAXN];
    int* y=new int[MAXN];
    
    int LCP(int,int);
    int LCS(int,int);
    
    int main(){
    	int T;
    	scanf("%d",&T);
    	for(int i=2;i<MAXN;i++)
    		lg[i]=lg[i>>1]+1;
    	while(T--){
    		scanf("%s",buf+1);
    		n=strlen(buf+1);
    		for(int i=1;i<=n;i++)
    			pf.s[i]=sf.s[n-i+1]=buf[i];
    		pf.Build();
    		sf.Build();
    		memset(lcnt,0,sizeof(int)*(n+1));
    		memset(rcnt,0,sizeof(int)*(n+1));
    		for(int len=1;(len<<1)<=n;len++){
    			for(int a=1,b;(b=a+len)<=n;a=b){
    				int p=LCP(a,b);
    				int s=LCS(a-1,b-1);
    				if(p+s>=len){
    					int l=std::max(a-len+1,a-s);
    					int r=std::min(a-(len-p),a);
    					++lcnt[l];
    					--lcnt[r+1];
    					++rcnt[l+(len<<1)-1];
    					--rcnt[r+(len<<1)];
    				}
    			}
    		}
    		for(int i=1;i<=n;i++){
    			lcnt[i]+=lcnt[i-1];
    			rcnt[i]+=rcnt[i-1];
    		}
    		intEx ans=0;
    		for(int i=1;i<n;i++)
    			ans+=1ll*rcnt[i]*lcnt[i+1];
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    
    int LCP(int a,int b){
    	return pf.LCP(a,b);
    }
    
    int LCS(int a,int b){
    	return sf.LCP(n-a+1,n-b+1);
    }
    
    int SuffixArray::LCP(int a,int b){
    	if(a<1||a>n||b<1||b>n)
    		return 0;
    	else if(a==b)
    		return n-a+1;
    	else{
    		a=rank[a];
    		b=rank[b];
    		if(a>b)
    			std::swap(a,b);
    		int p=lg[b-a];
    		++a;
    		return std::min(stmin[p][a],stmin[p][b-(1<<p)+1]);
    	}
    }
    
    void SuffixArray::Build(){
    	int m=127;
    	memset(x,0,sizeof(int)*(n+2));
    	memset(y,0,sizeof(int)*(n+2));
    	memset(cnt,0,sizeof(int)*(m+1));
    	for(int i=1;i<=n;i++)
    		++cnt[x[i]=s[i]];
    	for(int i=1;i<=m;i++)
    		cnt[i]+=cnt[i-1];
    	for(int i=n;i>=1;i--)
    		SA[cnt[x[i]]--]=i;
    	for(int k=1;k<n;k<<=1){
    		int p=0;
    		for(int i=n-k+1;i<=n;i++)
    			y[++p]=i;
    		for(int i=1;i<=n;i++)
    			if(SA[i]>k)
    				y[++p]=SA[i]-k;
    		memset(cnt,0,sizeof(int)*(m+1));
    		for(int i=1;i<=n;i++)
    			++cnt[x[i]];
    		for(int i=1;i<=m;i++)
    			cnt[i]+=cnt[i-1];
    		for(int i=n;i>=1;i--)
    			SA[cnt[x[y[i]]]--]=y[i];
    		std::swap(x,y);
    		x[SA[1]]=1;
    		p=1;
    		for(int i=2;i<=n;i++)
    			x[SA[i]]=(y[SA[i]]==y[SA[i-1]]&&y[SA[i]+k]==y[SA[i-1]+k])?p:++p;
    		if(p>=n)
    			break;
    		m=p;
    	}
    	for(int i=1;i<=n;i++)
    		rank[SA[i]]=i;
    	int k=0;
    	for(int i=1;i<=n;i++){
    		if(rank[i]==1)
    			continue;
    		if(k)
    			--k;
    		int j=SA[rank[i]-1];
    		while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k])
    			++k;
    		height[rank[i]]=k;
    	}
    	for(int i=1;(1<<i)<=n;i++){
    		for(int j=2;j<=n;j++){
    			stmin[i][j]=stmin[i-1][j];
    			if(j+(1<<(i-1))<=n)
    				stmin[i][j]=std::min(stmin[i][j],stmin[i-1][j+(1<<(i-1))]);
    		}
    	}
    }
    
    

  • 相关阅读:
    assert()函数用法总结
    UnityiOS键盘无法输入Emoji
    Unity 字体相关
    设计模式相关
    Unicode 与字符编码
    Unity 优化相关小结
    dedecms二次开发技巧汇总
    公司绝对不会告诉你的20个潜规则
    Ubuntu 如何自定义快捷键截图选定区域
    从一份简历就可以判断应聘者
  • 原文地址:https://www.cnblogs.com/rvalue/p/10946441.html
Copyright © 2020-2023  润新知