CF666E Forensic Examination 广义后缀自动机 + 线段树合并 + 树上倍增

题意:
给定一个串 $S$ 和若干个串 $T_{i}$
每次询问 $S[pl..pr]$ 在 $Tl..Tr$ 中出现的最多次数,以及出现次数最多的那个串的编号.

数据范围: 需要离线

题解:
首先,很常规的对 $T_{1}$ 到 $T_{rmax}$ 的所有字符串构建一个广义后缀自动机.
来一遍线段树合并,合并的权值是每个 $T$ 串出现的次数.
合并完毕后,再广义后缀自动机上的每个点的线段树上都能查到有哪些串能覆盖当前串.

把询问按照右端点排序,将 $S$ 匹配到广义后缀自动机当中,并通过倍增来将每个询问串进行定位..
最后 DFS 一遍算出答案即可.

还是很优美 + 复杂的.

Code:

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector> 
#define setIO(s) freopen(s".in","r",stdin)
#define maxn 2000000 
#define N 30 
#define sigma 26 
#define inf 1000000000

using namespace std;

vector <int> G[maxn]; 
char str[maxn],ss[maxn]; 
int str_len,m; 
int ss_len[maxn]; 
int fa[maxn][27]; 
int ans1[maxn],ans2[maxn]; 

struct Query{ int l,r,ans,pl,pr,id;   }arr[maxn];  
int cmp(Query a,Query b) { return a.pr<b.pr;  }

namespace Seg{
    struct Segment_Tree{ int l,r,maxv;  }node[maxn<<2]; 
    int root[maxn],nodes;  
    int newnode(){return ++nodes; }
    void update(int p,int l,int r,int &o){
        if(!o) o=newnode(); 
        if(l==r) { node[o].maxv+=1;  return ; }
        int mid=(l+r)>>1; 
        if(p<=mid) update(p,l,mid,node[o].l);
        else update(p,mid+1,r,node[o].r); 
        node[o].maxv=max(node[node[o].l].maxv,node[node[o].r].maxv); 
    }
    int merge(int x,int y,int l,int r){
        if(!x||!y) return x+y;    
        int o=newnode(),mid=(l+r)>>1; 
        if(l==r) { 
            node[o].maxv=node[x].maxv+node[y].maxv; 
            return o;  
        }
        node[o].l=merge(node[x].l,node[y].l,l,mid);
        node[o].r=merge(node[x].r,node[y].r,mid+1,r); 
        node[o].maxv=max(node[node[o].l].maxv,node[node[o].r].maxv); 
        return o; 
    }
    int query_max(int l,int r,int L,int R,int o) {
        if(!o||l>r||r<L||l>R) return 0; 
        if(l>=L&&r<=R) return node[o].maxv; 
        int mid=(l+r)>>1,res=0; 
        res=max(res,query_max(l,mid,L,R,node[o].l)); 
        res=max(res,query_max(mid+1,r,L,R,node[o].r));
        return res;  
    }
    int query_pos(int p,int l,int r,int L,int R,int o){
        if(l>r||r<L||l>R||!o||node[o].maxv<p) return inf; 
        if(l==r) return l; 
        int mid=(l+r)>>1,a; 
        if((a=query_pos(p,l,mid,L,R,node[o].l))!=inf) return a; 
        else return query_pos(p,mid+1,r,L,R,node[o].r); 
    }
}; 
namespace SAM{
    int f[maxn],dis[maxn],ch[maxn][N],tot,last;
    int C[maxn],rk[maxn]; 
    inline void init(){last=++tot; }
     void ins(int c){
        int p=last,np,nq;
        if(ch[p][c]){         
            int q=ch[p][c];               
            if(dis[q]==dis[p]+1) last=q;
            else
            {       
                nq=++tot,last=nq;
                f[nq]=f[q],f[q]=nq,dis[nq]=dis[p]+1;
                memcpy(ch[nq],ch[q],sizeof(ch[q]));
                while(p&&ch[p][c]==q)ch[p][c]=nq,p=f[p];           
            }
        }     
        else {
            np=++tot,last=np,dis[np]=dis[p]+1;
            while(p&&!ch[p][c]) ch[p][c]=np,p=f[p];
            if(!p) f[np]=1;
            else {   
                int q=ch[p][c];
                if(dis[q]==dis[p]+1) f[np]=q;
                else
                {
                    nq=++tot;
                    f[nq]=f[q],f[q]=f[np]=nq,dis[nq]=dis[p]+1;
                    memcpy(ch[nq],ch[q],sizeof(ch[q]));
                    while(p&&ch[p][c]==q) ch[p][c]=nq,p=f[p];
                }
            } 
        }
    }
}; 

int head[maxn],to[maxn],nex[maxn],edges; 
void addedge(int u,int v) { nex[++edges]=head[u],head[u]=edges,to[edges]=v; } 
void DFS(int u) {
    for(int v=head[u];v;v=nex[v]) 
    {
        DFS(to[v]); 
        Seg::root[u]=Seg::merge(Seg::root[u],Seg::root[to[v]],1,m); 
    }
    for(int i=0;i<G[u].size();++i) {
        int cur=G[u][i];
        int a=Seg::query_max(1,m,arr[cur].l,arr[cur].r,Seg::root[u]); 
        ans1[arr[cur].id] = a;
        if(!a) { ans2[arr[cur].id]=arr[cur].l;  continue; }   
        int b=Seg::query_pos(a,1,m,arr[cur].l,arr[cur].r,Seg::root[u]);
        ans2[arr[cur].id] = (b==inf)?0:b; 
    }
}

int main(){
    //setIO("input");      
    //==============================================================================
    scanf("%s",str+1),str_len = strlen(str + 1); 
    SAM::init();
    for(int i=1;i<=str_len;++i) SAM::ins(str[i]-'a'); 
    int i,j,queries; 
    for(SAM::init(),scanf("%d",&m),i=1;i<=m;++i) 
    {
        scanf("%s",ss+1),ss_len[i] = strlen(ss+1); 
        for(SAM::last=1,j=1;j<=ss_len[i];++j) 
        {
            SAM::ins(ss[j]-'a');
            Seg::update(i,1,m,Seg::root[SAM::last]);    
        }        
    } 
    for(i=2;i<=SAM::tot;++i) addedge(SAM::f[i],i),fa[i][0]=SAM::f[i]; 
    for(i=1;i<sigma;++i)
        for(j=2;j<=SAM::tot;++j) fa[j][i]=fa[fa[j][i-1]][i-1];         
    for(scanf("%d",&queries),i=1;i<=queries;++i)
        scanf("%d%d%d%d",&arr[i].l,&arr[i].r,&arr[i].pl,&arr[i].pr),arr[i].id=i; 
    sort(arr+1,arr+1+queries,cmp);           
    //=================================================================依次处理每个询问 
    int cur=1,p=1,q;  
    for(i=1;i<=queries;++i) {    
        for(;cur<=arr[i].pr;++cur) {
            int c=str[cur]-'a';                
            while(p && !SAM::ch[p][c]) p=SAM::f[p];
            if(!p) p=1; 
            else p=SAM::ch[p][c]; 
        } 
        q=p; 
        if(SAM::dis[p]>=(arr[i].pr-arr[i].pl+1)) {
            for(j=24;j>=0;--j) {
                if(SAM::dis[fa[p][j]] >= (arr[i].pr-arr[i].pl+1)) p=fa[p][j]; 
            }  
            G[p].push_back(i);   
        }
        p=q; 
    } 
    DFS(1); 
    for(i=1;i<=queries;++i) printf("%d %d
",ans2[i],ans1[i]);  
    return 0; 
}

  

原文地址:https://www.cnblogs.com/guangheli/p/10386611.html