• LOJ6031 字符串


    字符串

    (s)(w)为两字符串,定义:

    1. (w[l, r])表示字符串(w)在区间([l, r])中的子串;
    2. (w)(s)中出现的频率定义为(w)(s)中出现的次数;
    3. (f(s, w, l, r))表示(w[l, r])(s)中出现的频率。

    现在给定串(s)(m)个区间([l, r])和长度(k),你要回答(q)个询问,每个询问给你一个长度为(k)的字符串(w)和两个整数(a, b),求:

    [sumlimits_{i = a} ^ b f(s, w, l_i, r_i) ]

    对于(100\%)的数据,满足(n, m, k, q leq 10 ^ 5, sum w leq 10 ^ 5),字符串由小写英文字母构成。

    题解

    SAM + 根号分治的奇怪组合。

    http://jklover.hs-blog.cf/2020/04/16/Loj-6031-字符串/

    注意到这题不好做的地方在于:虽然串长有保证,但是每次的询问可能有很多。

    • (kleq q)时,暴力枚举子串。

      可以将([l,r])(m)个区间中出现的所有位置存在vector中,二分找出(a,b)的位置,做差即为所求。

    • (k>q)时,暴力枚举询问。

      就用经典的SAM定位法+倍增找祖先即可。

    时间复杂度(O(q(k+mlog n))=O(p+msqrt plog n))

    CO int N=2e5+10;
    int last=1,tot=1;
    array<int,26> ch[N];
    int fa[N],len[N],siz[N];
    
    void extend(int c){
    	int x=last,cur=last=++tot;
    	len[cur]=len[x]+1,siz[cur]=1;
    	for(;x and !ch[x][c];x=fa[x]) ch[x][c]=cur;
    	if(!x) {fa[cur]=1; return;}
    	int y=ch[x][c];
    	if(len[y]==len[x]+1) {fa[cur]=y; return;}
    	int clone=++tot;
    	ch[clone]=ch[y],fa[clone]=fa[y],len[clone]=len[x]+1;
    	fa[cur]=fa[y]=clone;
    	for(;ch[x][c]==y;x=fa[x]) ch[x][c]=clone;
    }
    
    int cnt[N],ord[N];
    
    void toposort(){
    	for(int i=1;i<=tot;++i) ++cnt[len[i]];
    	for(int i=1;i<=tot;++i) cnt[i]+=cnt[i-1];
    	for(int i=1;i<=tot;++i) ord[cnt[len[i]]--]=i;
    	for(int i=tot;i>=2;--i) siz[fa[ord[i]]]+=siz[ord[i]];
    }
    
    char s[N],w[N];
    int n,m,q,k,L[N],R[N];
    
    namespace Task1{
    	CO int K=320;
    	vector<int> vec[K][K];
    	
    	IN int calc(CO vector<int>&vec,int a,int b){
    		return upper_bound(vec.begin(),vec.end(),b)-lower_bound(vec.begin(),vec.end(),a);
    	}
    	void main(){
    		for(int i=1;i<=m;++i)
    			if(1<=L[i] and L[i]<=R[i] and R[i]<=k)
    				vec[L[i]][R[i]].push_back(i);
    		while(q--){
    			scanf("%s",w+1);
    			int a=read<int>()+1,b=read<int>()+1;
    			int64 ans=0;
    			for(int l=1;l<=k;++l){
    				int x=1;
    				for(int r=l;r<=k;++r){
    					x=ch[x][w[r]-'a'];
    					if(!x) break;
    					if(vec[l][r].empty()) continue;
    					if(vec[l][r].back()<a or vec[l][r].front()>b) continue;
    					ans+=(int64)siz[x]*calc(vec[l][r],a,b);
    				}
    			}
    			printf("%lld
    ",ans);
    		}
    	}
    }
    
    namespace Task2{
    	int anc[N][18];
    	vector<int> vec[N];
    	
    	int query(int x,int lim){
    		if(len[x]<lim) return 0;
    		for(int i=17;i>=0;--i)
    			if(len[anc[x][i]]>=lim) x=anc[x][i];
    		return siz[x];
    	}
    	void main(){
    		for(int i=2;i<=tot;++i){
    			int x=ord[i];
    			anc[x][0]=fa[x];
    			for(int j=1;j<=17;++j) anc[x][j]=anc[anc[x][j-1]][j-1];
    		}
    		while(q--){
    			scanf("%s",w+1);
    			int a=read<int>()+1,b=read<int>()+1;
    			for(int i=a;i<=b;++i) vec[R[i]].push_back(L[i]);
    			int64 ans=0;
    			int x=1,l=0;
    			for(int r=1;r<=k;++r){
    				int c=w[r]-'a';
    				if(ch[x][c]) x=ch[x][c],++l;
    				else{
    					while(x and !ch[x][c]) x=fa[x];
    					if(!x) x=1,l=0;
    					else l=len[x]+1,x=ch[x][c]; // important
    				}
    				for(int i:vec[r])
    					if(l>=r-i+1) ans+=query(x,r-i+1);
    			}
    			printf("%lld
    ",ans);
    			for(int i=a;i<=b;++i) vec[R[i]].clear();
    		}
    	}
    }
    
    int main(){
    	read(n),read(m),read(q),read(k);
    	scanf("%s",s+1);
    	for(int i=1;i<=n;++i) extend(s[i]-'a');
    	toposort();
    	for(int i=1;i<=m;++i) L[i]=read<int>()+1,R[i]=read<int>()+1;
    	if(k<=q) Task1::main();
    	else Task2::main();
    	return 0;
    }
    
  • 相关阅读:
    vue+iview简单实现获取要上传文件的Base64字符串
    com.microsoft.sqlserver.jdbc.SQLServerException: 必须执行该语句才能获得结果。
    Java入门2.1---面向对象的主线1---类及类的构成成分:属性、方法、构造器、代码块、内部类
    淘系自研前端研发工具 AppWorks 正式发布
    百度开源一款前端图片合成工具库:MI
    微软体验超棒的Fluent UI宣传短片,爱了爱了
    oracle的购买价格研究
    .NET Core 网络数据采集 -- 使用AngleSharp做html解析
    【译】Google Markdown书写风格指南
    我终于逃离了Node(你也能)
  • 原文地址:https://www.cnblogs.com/autoint/p/12731980.html
Copyright © 2020-2023  润新知