• 【题解】CF666E Forensic Examination


    CF666E Forensic Examination

    ( ext{Solution:})

    一个基本的思路是考虑如何对 SAM 上的点维护其子树内在每个串的出现次数。

    那这个东西是显然的线段树合并,打标记合并即可。

    接下来考虑如何迅速找到一个串的 parent 树上的对应点。那对 (S) 我们可以考虑把 (S) 的前缀点对应在 SAM 上的点编号预处理好,知道子串长度后从匹配到的点向上倍增跳就可以做到一个 $log $ 定位子串了。

    那想到这里这题就已经做完了。

    观察一下细节:如果不把 (S) 插入到 SAM 就直接匹配确定点会有很多特殊情况判断,比如匹配长度不足等等。我们可以考虑把 (S) 插入到 SAM 里面再找对应节点,就没有那么多问题了。

    还要注意线段树合并的时候维护两个值,最大值和位置。合并的时候只有叶子节点继承信息,其他节点需要 pushup 来维护。位置要尽量靠左维护。

    #include<bits/stdc++.h>
    using namespace std;
    const int SN=2e6+10;
    const int N=4e6+10;
    inline int Max(int x,int y){return x>y?x:y;}
    inline int Min(int x,int y){return x<y?x:y;}
    const int inf=(1<<30);
    typedef pair<int,int> pr;
    #define fi first
    #define se second
    #define mp make_pair
    inline pr Max(pr x,pr y){
    	if(x.se==y.se)return x.fi<y.fi?x:y;
    	return x.se>y.se?x:y;
    }
    int qy,m,pos[SN];
    namespace SGT{
    	int ls[N],rs[N],node;
    	pr mxpos[N];
    	inline void pushup(const int &x){mxpos[x]=Max(mxpos[ls[x]],mxpos[rs[x]]);}
    	void change(int &x,const int &L,const int &R,const int &pos){
    		if(!x)x=++node;
    		if(L==R){
    			mxpos[x].fi=L;
    			mxpos[x].se++;
    			return;
    		}
    		int mid=(L+R)>>1;
    		if(pos<=mid)change(ls[x],L,mid,pos);
    		else change(rs[x],mid+1,R,pos);
    		pushup(x);
    	}
    	int merge(const int &x,const int &y,const int &L,const int &R){
    		if(!x||!y)return x|y;
    		int p=++node;
    		int mid=(L+R)>>1;
    		if(L==R){
    			mxpos[p].fi=L;
    			mxpos[p].se=mxpos[x].se+mxpos[y].se;
    			return p;
    		}
    		ls[p]=merge(ls[x],ls[y],L,mid);
    		rs[p]=merge(rs[x],rs[y],mid+1,R);
    		pushup(p);return p;
    	}
    	pr query(const int &x,const int &L,const int &R,const int &l,const int &r){
    		if(L>=l&&R<=r){return mxpos[x];}
    		int mid=(L+R)>>1;
    		pr res=mp(-1,0);
    		if(l<=mid)res=query(ls[x],L,mid,l,r);
    		if(mid<r)res=Max(res,query(rs[x],mid+1,R,l,r));
    		return res;
    	}
    }
    using namespace SGT;
    namespace SAM{
    	int len[SN],pa[SN],ch[SN][26],rt[SN],tot=1,last=1,col[SN],f[SN][23];
    	vector<int>G[SN];
    	void insert(const int &c,const int &cl){
    		int p=last;int np=++tot;last=tot;
    		len[np]=len[p]+1;
    		for(;p&&!ch[p][c];p=pa[p])ch[p][c]=np;
    		if(!p)pa[np]=1;
    		else{
    			int q=ch[p][c];
    			if(len[q]==len[p]+1)pa[np]=q;
    			else{
    				int nq=++tot;
    				len[nq]=len[p]+1;
    				memcpy(ch[nq],ch[q],sizeof ch[q]);
    				pa[nq]=pa[q];pa[q]=pa[np]=nq;
    				for(;p&&ch[p][c]==q;p=pa[p])ch[p][c]=nq;
    			}
    		}
    	}
    	void dfs(int x){
    		for(auto v:G[x]){
    			f[v][0]=x;
    			for(int i=1;i<23;++i)f[v][i]=f[f[v][i-1]][i-1];
    			dfs(v);
    			rt[x]=merge(rt[x],rt[v],1,m);
    		}
    	}
    	void Build(){
    		for(int i=2;i<=tot;++i)G[pa[i]].push_back(i);
    		dfs(1);
    	}
    }
    using namespace SAM;
    char s[SN];
    string t[SN];
    void solve(int sl,int sr,int tl,int tr){
    	int spos=pos[sr];
    	if(spos<=1){
    		printf("%d 0
    ",tl);
    		return;
    	}
    	int lens=sr-sl+1;
    	int now=spos;
    	for(int i=22;~i;--i){if(now&&len[f[now][i]]>=lens)now=f[now][i];}
    	if(!now||now==1||len[now]<lens){
    		printf("%d 0
    ",tl);
    		return;
    	}
    	pr Ans=query(rt[now],1,m,tl,tr);
    	if(!Ans.se)Ans.fi=tl;
    	printf("%d %d
    ",Ans.fi,Ans.se);
    }
    int main(){
    	scanf("%s",s+1);
    	scanf("%d",&m);
    	for(int i=1;i<=m;++i){
    		last=1;
    		cin>>t[i];
    		int tlen=t[i].size();
    		for(int j=1;j<=tlen;++j){
    			int v=t[i][j-1]-'a';
    			insert(v,i);
    		}
    	}
    	int slen=strlen(s+1);
    	last=1;
    	for(int i=1;i<=slen;++i){
    		int v=s[i]-'a';
    		insert(v,0);
    	}
    	int now=1;
    	for(int i=1;i<=slen;++i){
    		int v=s[i]-'a';
    		now=ch[now][v];pos[i]=now;
    	}
    	for(int i=1;i<=m;++i){
    		int tlen=t[i].size();
    		int now=1;
    		for(int j=0;j<tlen;++j){
    			int v=t[i][j]-'a';
    			now=ch[now][v];
    			change(rt[now],1,m,i);
    		}
    	}
    	Build();
    	scanf("%d",&qy);
    	while(qy--){
    		int sl,sr,tl,tr;
    		scanf("%d%d%d%d",&tl,&tr,&sl,&sr);
    		solve(sl,sr,tl,tr);
    	}
    	return 0;
    }
    
  • 相关阅读:
    最大子数组问题(分治策略实现)
    Solving the Detached Many-to-Many Problem with the Entity Framework
    Working With Entity Framework Detached Objects
    Attaching detached POCO to EF DbContext
    如何获取qq空间最近访问人列表
    Health Monitoring in ASP.NET 2.0
    problem with displaying the markers on Google maps
    WebMatrix Database.Open… Close() and Dispose()
    Accessing and Updating Data in ASP.NET: Retrieving XML Data with XmlDataSource Control
    Create web setup project that has crystal reports and sql script run manually on client system
  • 原文地址:https://www.cnblogs.com/h-lka/p/15183311.html
Copyright © 2020-2023  润新知