Processing math: 100%

CF547E Mike and Friends

题面
英文题面
题解:
由于要统计字符串在某一串中的出现情况,不难想到用线段树合并。
对所有串建立广义SAM,然后拓扑排序,沿着link边向上合并即可。
每次查询,只需保存每个串终止位置的节点编号,在线段树上查询。
注意建立广义SAM时不能简单地把last设为1,还要注意不能每次都新加节点,
具体可以看我的代码。
时间复杂度:O(nlogn)
代码:

#include<bits/stdc++.h>
using namespace std;
#define re register int
#define F(x,y,z) for(re x=y;x<=z;x++)
#define FOR(x,y,z) for(re x=y;x>=z;x--)
typedef long long ll;
#define I inline void
#define IN inline int
#define C(x,y) memset(x,y,sizeof(x))
#define STS system("pause")
template<class D>I read(D &res){
	res=0;register D g=1;register char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-')g=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		res=(res<<3)+(res<<1)+(ch^48);
		ch=getchar();
	}
	res*=g;
}
namespace Tree{
	int root[404000],tot,lc[9090000],rc[9090000],w[9090000];
	#define lt lc[k],l,mid
	#define rt rc[k],mid+1,r
	I modi(int &k,int l,int r,int x){
		if(!k)k=++tot;w[k]++;
		if(l==r)return;
		re mid=(l+r)>>1;
		if(x<=mid)modi(lt,x);
		else modi(rt,x);
	}
	IN ques(int k,int l,int r,int x,int y){
		if(x>r||y<l||!k)return 0;
		if(x<=l&&r<=y)return w[k];
		re mid=(l+r)>>1;
		return ques(lt,x,y)+ques(rt,x,y);
	}
	IN merge(int x,int y){
		if(!x||!y)return x+y;
		re now=++tot;
		w[now]=w[x]+w[y];lc[now]=merge(lc[x],lc[y]);rc[now]=merge(rc[x],rc[y]);
		return now;
	}
};
namespace SAM{
	int ch[404000][27],len[404000],link[404000],tot,las,p,q,cur,cle;
	int buc[404000],sa[404000],id[202000];
	I init(){
		las=1;tot=1;
	}
	I add(int x){
		if(ch[las][x]){
			p=las;q=ch[p][x];
			if(len[p]+1==len[q]){las=q;return;}
			cle=++tot;len[cle]=len[p]+1,link[cle]=link[q];
			memcpy(ch[cle],ch[q],sizeof(ch[q]));
			while(p&&ch[p][x]==q)ch[p][x]=cle,p=link[p];
			las=cle;link[q]=cle;return;
		}
		len[cur=++tot]=len[las]+1;p=las;las=cur;
		while(p&&!ch[p][x])ch[p][x]=cur,p=link[p];
		if(!p){link[cur]=1;return;}
		q=ch[p][x];
		if(len[p]+1==len[q]){link[cur]=q;return;}
		cle=++tot;len[cle]=len[p]+1,link[cle]=link[q];
		memcpy(ch[cle],ch[q],sizeof(ch[q]));
		while(p&&ch[p][x]==q)ch[p][x]=cle,p=link[p];
		link[cur]=link[q]=cle;
	}
	I sort(){
		F(i,1,tot)buc[len[i]]++;
		F(i,1,tot)buc[i]+=buc[i-1];
		FOR(i,tot,1)sa[buc[len[i]]--]=i;
		FOR(i,tot,1){
			if(sa[i]==1)continue;Tree::root[link[sa[i]]]=Tree::merge(Tree::root[link[sa[i]]],Tree::root[sa[i]]);
		}
	}
};
char c[202000];
int n,m,X,Y,W;
int main(){
	read(n);read(m);
	SAM::init();
	F(i,1,n){
		scanf("%s",c+1);SAM::las=1;re len=strlen(c+1);
		F(j,1,len)SAM::add(c[j]-'a'+1),Tree::modi(Tree::root[SAM::las],1,n,i);
		SAM::id[i]=SAM::las;
	}
	SAM::sort();
	while(m--){
		read(X);read(Y);read(W);
		printf("%d\n",Tree::ques(Tree::root[SAM::id[W]],1,n,X,Y));
	}
	return 0;
}
posted @   Purple_wzy  阅读(195)  评论(0编辑  收藏  举报
编辑推荐:
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
· C# 深度学习:对抗生成网络(GAN)训练头像生成模型
· .NET 适配 HarmonyOS 进展
· .NET 进程 stackoverflow异常后,还可以接收 TCP 连接请求吗?
阅读排行:
· 本地部署 DeepSeek:小白也能轻松搞定!
· 基于DeepSeek R1 满血版大模型的个人知识库,回答都源自对你专属文件的深度学习。
· 在缓慢中沉淀,在挑战中重生!2024个人总结!
· 如何给本地部署的DeepSeek投喂数据,让他更懂你
· 大人,时代变了! 赶快把自有业务的本地AI“模型”训练起来!
点击右上角即可分享
微信分享提示