题面:
给你一个串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