• Codeforces.666E.Forensic Examination(广义后缀自动机 线段树合并)


    题目链接

    (Description)

    给定串(S)(m)个串(T_i)(Q)次询问,每次询问(l,r,p_l,p_r),求(S[p_lsim p_r])(T_lsim T_r)中的哪个串出现次数最多,输出最多次数以及它是(T)中的第几个。若最多的有多个,输出下标最小的。

    (Solution)

    挺好的题吧

    (T)个串建SAM,然后要求出SAM每个节点上(|right|)最大的是哪个串。
    每个节点的(|right|)可以在DFS parent树时合并子节点得到。如果用线段树,区间(|right|)最大的是哪个串也可以维护出来。
    那么可以离线,在每个点处处理该点上的询问,边DFS边合并线段树得到所有答案。(当然可持久化一下在线也行?)

    怎么得到(S[p_lsim p_r])在SAM上的匹配节点呢?
    维护一个节点指针(p),拿(S)在SAM上尽可能匹配,匹配不了就跳(fa)。这样能保证当前(S)的后缀((S[i]))一定在(p)节点出现了。
    所以在(i)这里处理(p_r=i)的询问。只要我们从当前节点(p)一直跳(fa),就能找到(S[p_l,p_r])所在的节点,其答案就是该节点的(|right|)状态。可以用倍增实现。
    (就是从(S[1,p_r])所匹配的节点(p)往上跳,跳到从上往下第一个(len_xgeq p_r-p_l+1)的节点(x)(x)就是(S[p_l,p_r])所在的节点)

    如果出现次数为(0)的话也要输出最靠前的(即(l))。=-=

    唉 6点多写完代码调到现在 心累

    //529ms	56700KB
    #include <cstdio>
    #include <cctype>
    #include <cstring>
    #include <algorithm>
    #define gc() getchar()
    #define Bit 16
    const int N=5e5+5,M=5e4+5,S=M<<1;
    
    int m,root[S],fa[S][18];
    char s[N],tmp[M];
    struct Edge
    {
    	int Enum,H[N],nxt[N],to[N];
    	inline void AddEdge(int u,int v){
    		to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;
    	}
    }Pos,Qy;
    struct Edge2
    {
    	int Enum,H[S],nxt[S],to[S];
    	inline void AddEdge(int u,int v){
    		to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;
    	}
    }Par;
    struct Queries{
    	int l,r,pl,pr;
    }q[N];
    struct Suffix_Automaton
    {
    	int tot,las,fa[S],son[S][26],len[S];
    
    	Suffix_Automaton() {tot=las=1;}
    	void Insert(int c)
    	{
    		int np=++tot,p=las; len[las=np]=len[p]+1;
    		for(; p&&!son[p][c]; p=fa[p]) son[p][c]=np;
    		if(!p) fa[np]=1;
    		else
    		{
    			int q=son[p][c];
    			if(len[q]==len[p]+1) fa[np]=q;
    			else
    			{
    				int nq=++tot; len[nq]=len[p]+1;
    				memcpy(son[nq],son[q],sizeof son[q]);
    				fa[nq]=fa[q], fa[q]=fa[np]=nq;
    				for(; son[p][c]==q; p=fa[p]) son[p][c]=nq;
    			}
    		}
    	}
    }sam;
    struct Node
    {
    	int val,id;
    	bool operator <(const Node &x)const{
    		return val<x.val||(val==x.val&&id>x.id);
    	}
    }Ans[N];
    struct Segment_Tree
    {
    	#define S M*17
    	#define lson son[x][0]
    	#define rson son[x][1]
    	int tot,son[S][2];
    	Node node[S];
    	#undef S
    	#define Update(x) node[x]=std::max(node[lson],node[rson]);
    	void Insert(int &x,int l,int r,int pos)
    	{
    		x=++tot;
    		if(l==r) return (void)(node[x]=(Node){1,pos});
    		int m=l+r>>1;
    		pos<=m ? Insert(lson,l,m,pos) : Insert(rson,m+1,r,pos);
    		Update(x);//Update 
    	}
    	int Merge(int x,int y)
    	{
    		if(!x||!y) return x^y;
    		if(!lson&&!rson) return node[x].val+=node[y].val, x;//叶节点,合并right 
    		lson=Merge(lson,son[y][0]), rson=Merge(rson,son[y][1]);
    		Update(x); return x;
    	}
    	Node Query(int x,int l,int r,int L,int R)
    	{
    		if(!x) return (Node){0,L};
    		if(L<=l && r<=R) return node[x];
    		int m=l+r>>1;
    		if(L<=m)
    			if(m<R) return std::max(Query(lson,l,m,L,R),Query(rson,m+1,r,L,R));
    			else return Query(lson,l,m,L,R);
    		return Query(rson,m+1,r,L,R);
    	}
    }T;
    
    inline int read()
    {
    	int now=0;register char c=gc();
    	for(;!isdigit(c);c=gc());
    	for(;isdigit(c);now=now*10+c-'0',c=gc());
    	return now;
    }
    void DFS(int x)
    {
    	for(int i=Par.H[x]; i; i=Par.nxt[i])
    		DFS(Par.to[i]), root[x]=T.Merge(root[x],root[Par.to[i]]);
    	for(int i=Pos.H[x],id; i; i=Pos.nxt[i])
    		id=Pos.to[i], Ans[id]=T.Query(root[x],1,m,q[id].l,q[id].r);
    }
    
    int main()
    {
    	scanf("%s",s+1); int n=strlen(s+1);
    	m=read();
    	for(int i=1; i<=m; ++i)
    	{
    		scanf("%s",tmp), sam.las=1;
    		for(int j=0,l=strlen(tmp); j<l; ++j)
    			sam.Insert(tmp[j]-'a'), T.Insert(root[sam.las],1,m,i);//不就是每位的|right[las]|=1吗→_→我在纠结什么 
    			//就是las节点的线段树上,i位置的|right|=1,给i位置+1
    	}
    	int Q=read();
    	for(int i=1; i<=Q; ++i)
    		q[i]=(Queries){read(),read(),read(),read()}, Qy.AddEdge(q[i].pr,i);
    	int lim=sam.tot;
    	for(int x=2; x<=lim; ++x) Par.AddEdge(fa[x][0]=sam.fa[x],x);
    	for(int i=1; i<=Bit; ++i)
    		for(int x=2; x<=lim; ++x)
    			fa[x][i]=fa[fa[x][i-1]][i-1];
    	for(int c,now=0,p=1,i=1; i<=n; ++i)
    	{
    		if(sam.son[p][c=s[i]-'a']) p=sam.son[p][c], ++now;//!!!靠 这写错调了两个小时 唉 心累 
    		else
    		{
    			for(c=s[i]-'a'; p&&!sam.son[p][c]; p=sam.fa[p]);
    			if(!p) {p=1, now=0; continue;}
    			now=sam.len[p]+1, p=sam.son[p][c];
    		}
    		for(int j=Qy.H[i],len,id; j; j=Qy.nxt[j])
    		{
    			id=Qy.to[j];
    			if(now<(len=q[id].pr-q[id].pl+1)) continue;
    			int x=p;
    			for(int i=Bit; ~i; --i)
    				if(sam.len[fa[x][i]]>=len) x=fa[x][i];
    			Pos.AddEdge(x,id);
    		}
    	}
    	DFS(1);
    	for(int i=1; i<=Q; ++i)
    		if(!Ans[i].val) printf("%d 0
    ",q[i].l);
    		else printf("%d %d
    ",Ans[i].id,Ans[i].val);
    
    	return 0;
    }
    
  • 相关阅读:
    [原创]益盟软件测试流程培训
    一图教你如何管理自己的时间
    [原创]质量控制思维导图
    [原创]常用Web服务器日志工具介绍
    [原创]常见的测试类型思维导图
    [原创]软件项目研发控制流程(草稿)
    [原创]Top 15 free SQL Injection Scanners
    [原创]2010年12月测试团队培训表
    [原创]Firefox Throttle 网络带宽限速工具介绍
    [原创]对5W1H分析法应用到工作中的理解
  • 原文地址:https://www.cnblogs.com/SovietPower/p/9368392.html
Copyright © 2020-2023  润新知