• 【CF666E】Forensic Examination(后缀自动机,线段树合并)


    【CF666E】Forensic Examination(后缀自动机,线段树合并)

    题面

    洛谷
    CF

    翻译:
    给定一个串(S)和若干个串(T_i)
    每次询问(S[pl..pr])(T_l..T_r)中出现的最多次数,以及出现次数最多的那个串的编号。

    题解

    好题啊。

    我们对于所有的(T)串构建出广义后缀自动机之后
    (S)丢到(SAM)上匹配,对于每组询问,
    相当于回答(S[pl..pr])所代表的节点的(right)集合所代表的串的众数是哪个串,以及这个众数出现的次数。
    考虑如何维护(right)集合关于每个(T)的出现次数

    我们对于每个节点开一棵线段树,线段树的值域是(1..m),用来维护每个(T)串的出现次数。
    每个节点维护的值就是(T)串的最多出现次数。
    这样就挺好做了,(right)集合只需要线段树合并就可以快速求得。
    对于每个询问挂链,然后(dfs)一遍(parent)树的时候,顺便在线段树上查询一下区间最大就行了。

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<set>
    #include<map>
    #include<vector>
    #include<queue>
    using namespace std;
    #define ll long long
    #define RG register
    #define MAX 1111111
    inline int read()
    {
        RG int x=0,t=1;RG char ch=getchar();
        while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
        if(ch=='-')t=-1,ch=getchar();
        while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
        return x*t;
    }
    struct SAM
    {
    	struct Node
    	{
    		int son[26];
    		int ff,len;
    	}t[MAX];
    	int last,tot;
    	void init(){last=tot=1;}
    	void extend(int c)
    	{
    		int p=last,np=++tot;last=np;
    		t[np].len=t[p].len+1;
    		while(p&&!t[p].son[c])t[p].son[c]=np,p=t[p].ff;
    		if(!p)t[np].ff=1;
    		else
    		{
    			int q=t[p].son[c];
    			if(t[q].len==t[p].len+1)t[np].ff=q;
    			else
    			{
    				int nq=++tot;
    				t[nq]=t[q];
    				t[nq].len=t[p].len+1;
    				t[q].ff=t[np].ff=nq;
    				while(p&&t[p].son[c]==q)t[p].son[c]=nq,p=t[p].ff;
    			}
    		}
    	}
    }SAM;
    
    int n,m,f[22][MAX];
    struct Data{int v,p;}ans[MAX];
    bool operator<(Data a,Data b){return (a.v<b.v)||(a.v==b.v&&a.p>b.p);}
    struct SegmentTree{int ls,rs;Data v;}t[MAX<<4];
    int Tot,rt[MAX];
    struct query{int l,r,pl,pr;}q[MAX];
    char S[MAX],T[MAX];
    void Modify(int &x,int l,int r,int p)
    {
    	if(!x)x=++Tot;
    	if(l==r){t[x].v.v++;t[x].v.p=p;return;}
    	int mid=(l+r)>>1;
    	if(p<=mid)Modify(t[x].ls,l,mid,p);
    	else Modify(t[x].rs,mid+1,r,p);
    	t[x].v=max(t[t[x].ls].v,t[t[x].rs].v);
    }
    void Merge(int &x,int y)
    {
    	if(!x||!y){x|=y;return;}
    	if(!t[x].ls&&!t[x].rs){t[x].v.v+=t[y].v.v;return;}
    	Merge(t[x].ls,t[y].ls);Merge(t[x].rs,t[y].rs);
    	t[x].v=max(t[t[x].ls].v,t[t[x].rs].v);
    }
    Data Query(int x,int l,int r,int L,int R)
    {
    	if(L==l&&r==R)return t[x].v;
    	int mid=(l+r)>>1;
    	if(R<=mid)return Query(t[x].ls,l,mid,L,R);
    	if(L>mid)return Query(t[x].rs,mid+1,r,L,R);
    	return max(Query(t[x].ls,l,mid,L,mid),Query(t[x].rs,mid+1,r,mid+1,R));
    }
    
    struct Link
    {
    	struct Line{int v,next;}e[MAX];
    	int h[MAX],cnt;
    	void Add(int u,int v){e[++cnt]=(Line){v,h[u]};h[u]=cnt;}
    }Par,Qy,Aw;
    
    void dfs(int u)
    {
    	for(int i=Par.h[u];i;i=Par.e[i].next)
    		dfs(Par.e[i].v),Merge(rt[u],rt[Par.e[i].v]);
    	for(int i=Aw.h[u];i;i=Aw.e[i].next)
    		ans[Aw.e[i].v]=Query(rt[u],1,m,q[Aw.e[i].v].l,q[Aw.e[i].v].r);
    }
    
    int main()
    {
    	scanf("%s",S+1);n=strlen(S+1);
    	m=read();SAM.init();
    	for(int i=1;i<=m;++i)
    	{
    		SAM.last=1;scanf("%s",T+1);
    		for(int j=1,l=strlen(T+1);j<=l;++j)
    			SAM.extend(T[j]-97),Modify(rt[SAM.last],1,m,i);
    	}
    	int Q=read();
    	for(int i=1;i<=Q;++i)
    	{
    		q[i]=(query){read(),read(),read(),read()};
    		Qy.Add(q[i].pr,i);
    	}
    	for(int i=2;i<=SAM.tot;++i)Par.Add(f[0][i]=SAM.t[i].ff,i);
    	for(int i=1;i<22;++i)
    		for(int j=1;j<=SAM.tot;++j)f[i][j]=f[i-1][f[i-1][j]];
    	for(int i=1,nw=1,len=0;i<=n;++i)
    	{
    		int c=S[i]-97;
    		while(nw&&!SAM.t[nw].son[c])nw=SAM.t[nw].ff,len=SAM.t[nw].len;
    		if(!nw){nw=1,len=0;continue;}
    		nw=SAM.t[nw].son[c];len+=1;
    		for(int j=Qy.h[i];j;j=Qy.e[j].next)
    		{
    			int v=Qy.e[j].v,u=nw;
    			if(len<q[v].pr-q[v].pl+1)continue;
    			for(int k=21;~k;--k)
    				if(SAM.t[f[k][u]].len>=q[v].pr-q[v].pl+1)u=f[k][u];
    			Aw.Add(u,v);
    		}
    	}
    	dfs(1);
    	for(int i=1;i<=Q;++i)
    	{
    		if(!ans[i].v)ans[i].p=q[i].l;
    		printf("%d %d
    ",ans[i].p,ans[i].v);
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    grep如何忽略.svn目录,以及如何忽略多个目录
    CSS写的提示框(兼容火狐IE等各大浏览器)
    校验IPv4和IPv6地址和URL地址
    input框设置onInput事件只能输入数字,能兼容火狐IE9
    $(function(){})、$(document).ready(function(){})....../ ready和onload的区别
    jQuery EasyUI 教程-Tooltip(提示框)
    小知识随手记(一)
    自动换行效果对比
    getComputedStyle与currentStyle获取样式(style/class)
    弹出层框架layer快速使用
  • 原文地址:https://www.cnblogs.com/cjyyb/p/8805044.html
Copyright © 2020-2023  润新知