CF666E Forensic Examination SAM+倍增,线段树和并

题面:

  给你一个串S以及一个字符串数组T[1..m],q次询问,每次问S的子串S[p_l..p_r]在T[l..r]中的哪个串里的出现次数最多,并输出出现次数。如有多解输出最靠前的那一个。

 

分析:

  第一次见这道题时,由于对算法十分陌生,就将其压进了任务计划,这一天又提到了这道题,这才算是重见天日。

  数据结构题,越来越注重多种数据结构的搭配使用。搭配使用呢,我们就要知道各种数据结构的功能与适用范围,下面是这道题一个理想的思考过程(虽然我是抄的题解……)

  首先,一个模式串的区间要在多个串上进行匹配,我们可以想到多串构造的广义后缀自动机。

  由于我们要对一个模式串的区间,询问:在一个区间的匹配串中,最多出现在哪个匹配串,以及出现了多少次。(有点拗口)

  我们需要使用一个支持区间查询的数据结构,并且,我们需要支持在SAM上,查询一个点的right集合中的所有点的信息。

  我们采取的策略是,对于SAM的每一个点,我们建一棵值域为1~m的权值线段树,然后离线处理询问,将询问挂链(开vector存下来应该也可以),然后自底向上线段树合并,回答询问。

  但是,我们对询问应该怎样调整一下呢,总不能对于每个区间都在广义SAM上跑一遍吧~

  我们要总地把模式串S在SAM上跑一遍,只需要对每个询问记录右端点qr,当S匹配到这个点的时候,将所有右端点为qr的询问挂到链上,(为什么不直接挂链呢,因为我们不能随便挂链,需要挂到SAM上最早包含模式串这一段的那个点上(也就是匹配到当前点时,parent树上最远的一个len大于这一段长度的点上)),这里言语很难解释,只有自己体会这样做的妙处。但是这步操作怎么做呢,暴跳的话理论复杂度是过不去的(但实际上过得去),所以我们最好用一个倍增数组来做这个。(万一被hack掉可就不好了)

  然后这道题跑线段树合并即可,由于是抄的题解代码,所以代码风格不太对(除了压行风格)请理智吸收。

代码:

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 const int N=1000005;
  4 struct SAM{
  5     struct node{
  6         int son[26],fa,l;
  7     }t[N];int lst,cnt;
  8     void init(){lst=cnt=1;}
  9     void ins(int c){
 10         int p=lst,np=++cnt;lst=np;
 11         t[np].l=t[p].l+1;
 12         for(;p&&!t[p].son[c];p=t[p].fa) 
 13         t[p].son[c]=np;if(!p) t[np].fa=1;
 14         else{
 15             int q=t[p].son[c];
 16             if(t[q].l==t[p].l+1) t[np].fa=q;
 17             else{
 18                 int nq=++cnt;t[nq]=t[q];
 19                 t[nq].l=t[p].l+1;
 20                 t[q].fa=t[np].fa=nq;
 21                 while(p&&t[p].son[c]==q)
 22                 t[p].son[c]=nq,p=t[p].fa;
 23             }
 24         }
 25     }
 26 }sam;int n,m,f[N][22];
 27 struct data{int v,p;}ans[N];
 28 bool operator<(data a,data b){
 29     return (a.v<b.v)||(a.v==b.v&&a.p>b.p);
 30 } struct segtree{int ls,rs;data v;}t[N<<4];
 31 struct que{int l,r,pl,pr;}q[N];
 32 int tot,rt[N];char S[N],T[N];
 33 void update(int &x,int l,int r,int p){
 34     if(!x) x=++tot;if(l==r)
 35     {t[x].v.v++;t[x].v.p=p;return;}
 36     int mid=l+r>>1;
 37     if(p<=mid) update(t[x].ls,l,mid,p);
 38     else update(t[x].rs,mid+1,r,p);
 39     t[x].v=max(t[t[x].ls].v,t[t[x].rs].v);
 40 } int merge(int x,int y){
 41     if(!y||!x) return x|y;
 42     if(!t[x].ls&&!t[x].rs)
 43     {t[x].v.v+=t[y].v.v;return x;}
 44     t[x].ls=merge(t[x].ls,t[y].ls);
 45     t[x].rs=merge(t[x].rs,t[y].rs);
 46     t[x].v=max(t[t[x].ls].v,t[t[x].rs].v);
 47     return x;
 48 } data query(int x,int l,int r,int L,int R){
 49     if(L<=l&&r<=R) return t[x].v;
 50     int mid=l+r>>1;if(R<=mid)
 51     return query(t[x].ls,l,mid,L,R);
 52     if(L>mid) return query(t[x].rs,mid+1,r,L,R);
 53     return max(query(t[x].ls,l,mid,L,R),
 54     query(t[x].rs,mid+1,r,L,R));
 55 } struct link{
 56     struct line{int y,nxt;}e[N];
 57     int h[N],c;void add(int x,int y)
 58     {e[++c]=(line){y,h[x]};h[x]=c;}
 59 }par,qy,aw;void dfs(int x){
 60     for(int i=par.h[x];i;i=par.e[i].nxt)
 61     dfs(par.e[i].y),rt[x]=merge(rt[x],rt[par.e[i].y]);
 62     for(int i=aw.h[x];i;i=aw.e[i].nxt)
 63     ans[aw.e[i].y]=query(rt[x],1,m,q[aw.e[i].y].l,
 64     q[aw.e[i].y].r);return ;
 65 } int main(){
 66     scanf("%s",S+1);n=strlen(S+1);
 67     scanf("%d",&m);sam.init();
 68     for(int i=1;i<=m;i++){
 69         sam.lst=1;scanf("%s",T+1);
 70         for(int j=1,l=strlen(T+1);j<=l;j++)
 71         sam.ins(T[j]-97),update(rt[sam.lst],1,m,i);
 72     } int Q;scanf("%d",&Q);
 73     for(int i=1,l,r,pl,pr;i<=Q;i++){
 74         scanf("%d%d%d%d",&l,&r,&pl,&pr);
 75         q[i]=(que){l,r,pl,pr};qy.add(q[i].pr,i);
 76     } for(int i=2;i<=sam.cnt;i++)
 77     par.add(f[i][0]=sam.t[i].fa,i);
 78     for(int i=1;i<22;i++)
 79     for(int j=1;j<=sam.cnt;j++)
 80     f[j][i]=f[f[j][i-1]][i-1];
 81     for(int i=1,nw=1,len=0;i<=n;i++){
 82         int c=S[i]-97;
 83         while(nw&&!sam.t[nw].son[c])
 84         nw=sam.t[nw].fa,len=sam.t[nw].l;
 85         if(!nw){nw=1,len=0;continue;}
 86         nw=sam.t[nw].son[c];len+=1;
 87         for(int j=qy.h[i];j;j=qy.e[j].nxt){
 88             int y=qy.e[j].y,x=nw,tmp;
 89             tmp=q[y].pr-q[y].pl+1;
 90             if(len<tmp) continue;
 91             for(int k=21;~k;k--)
 92             if(sam.t[f[x][k]].l>=tmp) x=f[x][k];
 93             aw.add(x,y);
 94         }
 95     } dfs(1);
 96     for(int i=1;i<=Q;i++){
 97         if(!ans[i].v) ans[i].p=q[i].l;
 98         printf("%d %d\n",ans[i].p,ans[i].v);
 99     } return 0;
100 }
理论上过得去但跑的慢
 1 //这是CF666E理论上过不去(但实际上过得去)的代码 
 2 #include<bits/stdc++.h>
 3 using namespace std;
 4 const int N=1000005;
 5 struct SAM{
 6     struct node{
 7         int son[26],fa,l;
 8     }t[N];int lst,cnt;
 9     void init(){lst=cnt=1;}
10     void ins(int c){
11         int p=lst,np=++cnt;lst=np;
12         t[np].l=t[p].l+1;
13         for(;p&&!t[p].son[c];p=t[p].fa) 
14         t[p].son[c]=np;if(!p) t[np].fa=1;
15         else{
16             int q=t[p].son[c];
17             if(t[q].l==t[p].l+1) t[np].fa=q;
18             else{
19                 int nq=++cnt;t[nq]=t[q];
20                 t[nq].l=t[p].l+1;
21                 t[q].fa=t[np].fa=nq;
22                 while(p&&t[p].son[c]==q)
23                 t[p].son[c]=nq,p=t[p].fa;
24             }
25         }
26     }
27 }sam;int n,m,f[N][22];
28 struct data{int v,p;}ans[N];
29 bool operator<(data a,data b){
30     return (a.v<b.v)||(a.v==b.v&&a.p>b.p);
31 } struct segtree{int ls,rs;data v;}t[N<<4];
32 struct que{int l,r,pl,pr;}q[N];
33 int tot,rt[N];char S[N],T[N];
34 void update(int &x,int l,int r,int p){
35     if(!x) x=++tot;if(l==r)
36     {t[x].v.v++;t[x].v.p=p;return;}
37     int mid=l+r>>1;
38     if(p<=mid) update(t[x].ls,l,mid,p);
39     else update(t[x].rs,mid+1,r,p);
40     t[x].v=max(t[t[x].ls].v,t[t[x].rs].v);
41 } int merge(int x,int y){
42     if(!y||!x) return x|y;
43     if(!t[x].ls&&!t[x].rs)
44     {t[x].v.v+=t[y].v.v;return x;}
45     t[x].ls=merge(t[x].ls,t[y].ls);
46     t[x].rs=merge(t[x].rs,t[y].rs);
47     t[x].v=max(t[t[x].ls].v,t[t[x].rs].v);
48     return x;
49 } data query(int x,int l,int r,int L,int R){
50     if(L<=l&&r<=R) return t[x].v;
51     int mid=l+r>>1;if(R<=mid)
52     return query(t[x].ls,l,mid,L,R);
53     if(L>mid) return query(t[x].rs,mid+1,r,L,R);
54     return max(query(t[x].ls,l,mid,L,R),
55     query(t[x].rs,mid+1,r,L,R));
56 } struct link{
57     struct line{int y,nxt;}e[N];
58     int h[N],c;void add(int x,int y)
59     {e[++c]=(line){y,h[x]};h[x]=c;}
60 }par,qy,aw;void dfs(int x){
61     for(int i=par.h[x];i;i=par.e[i].nxt)
62     dfs(par.e[i].y),rt[x]=merge(rt[x],rt[par.e[i].y]);
63     for(int i=aw.h[x];i;i=aw.e[i].nxt)
64     ans[aw.e[i].y]=query(rt[x],1,m,q[aw.e[i].y].l,
65     q[aw.e[i].y].r);return ;
66 } int main(){
67     scanf("%s",S+1);n=strlen(S+1);
68     scanf("%d",&m);sam.init();
69     for(int i=1;i<=m;i++){
70         sam.lst=1;scanf("%s",T+1);
71         for(int j=1,l=strlen(T+1);j<=l;j++)
72         sam.ins(T[j]-97),update(rt[sam.lst],1,m,i);
73     } int Q;scanf("%d",&Q);
74     for(int i=1,l,r,pl,pr;i<=Q;i++){
75         scanf("%d%d%d%d",&l,&r,&pl,&pr);
76         q[i]=(que){l,r,pl,pr};qy.add(q[i].pr,i);
77     } for(int i=2;i<=sam.cnt;i++)
78     par.add(f[i][0]=sam.t[i].fa,i);
79     for(int i=1,nw=1,len=0;i<=n;i++){
80         int c=S[i]-97;
81         while(nw&&!sam.t[nw].son[c])
82         nw=sam.t[nw].fa,len=sam.t[nw].l;
83         if(!nw){nw=1,len=0;continue;}
84         nw=sam.t[nw].son[c];len+=1;
85         for(int j=qy.h[i];j;j=qy.e[j].nxt){
86             int y=qy.e[j].y,x=nw,tmp;
87             tmp=q[y].pr-q[y].pl+1;
88             if(len<tmp) continue;
89             while(sam.t[f[x][0]].l>=tmp) x=f[x][0];
90             aw.add(x,y);
91         }
92     } dfs(1);
93     for(int i=1;i<=Q;i++){
94         if(!ans[i].v) ans[i].p=q[i].l;
95         printf("%d %d\n",ans[i].p,ans[i].v);
96     } return 0;
97 }
理论上过不去但跑得更快

 

转载于:https://www.cnblogs.com/Alan-Luo/p/10421585.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值