zoukankan      html  css  js  c++  java
  • 【题解】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;
    }
    
  • 相关阅读:
    洛谷 P1604 B进制星球
    629D
    。。。。。
    新安排
    计算几何的两道题
    cf C题
    昨天
    最近几天的安排
    最近几天
    杂论——持续更新。
  • 原文地址:https://www.cnblogs.com/h-lka/p/15183311.html
Copyright © 2011-2022 走看看