【CF666E】Forensic Examination - 广义后缀自动机+线段树合并
广义SAM专题的最后一题了……呼
题意:
给出一个长度为$n$的串$S$和$m$个串$T_{1\cdots m}$,给出$q$个询问$l,r,pl,pr$,询问$S[pl\cdots pr]$在$T_l\cdots T_r$中哪个串出现次数最多,出现了多少次。
$1\leq n,q\leq 10^5,1\leq m,\sum|T|\leq 10^4$
串中只会出现小写字母
题解:
神题啊……放图镇楼
先对T串建出广义SAM,然后把S放到上面匹配,求出每个字符所代表的节点,那么每次查询就相当于求这一段字符在SAM上对应的节点的right集合包含的字符串的众数是哪个串,显然这是parent树上的一个子树众数问题;
考虑如何维护right集合在所有$T$中的出现次数,可以对每一个节点开一棵线段树,维护每个T串的出现次数的最大值,这样子在parent树上从下往上线段树合并即可求出right集合;
把询问离线按照右端点排序,把询问标记打在parent树上,最后dfs一遍合并+处理询问即可;
口胡起来不难但是写起来……超爽!
代码:
1 #include<algorithm>
2 #include<iostream>
3 #include<cstring>
4 #include<cstdio>
5 #include<vector>
6 #include<cmath>
7 #include<queue>
8 #define inf 2147483647
9 #define eps 1e-9
10 using namespace std;
11 typedef long long ll;
12 typedef double db;
13 struct task{
14 int v,id;
15 task(){v=id=0;}
16 friend bool operator <(task a,task b){
17 return a.v==b.v?a.id>b.id:a.v<b.v;
18 }
19 }ans[500001];
20 struct qu{
21 int l,r,ql,qr;
22 }q[500001];
23 struct edge{
24 int v,next;
25 }a[1000001];
26 struct node{
27 int ls,rs;
28 task v;
29 }t[4000001];
30 int n,m,Q,len,ch,nw=1,tote=0,tot=1,rt=1,cnt=0,last,head[1000001],rts[1000001],son[1000001][26],fa[1000001],mx[1000001],f[1000001][20];
31 vector<int>qrs[500001];
32 vector<int>as[500001];
33 char st[500001],tt[500001];
34 void add(int u,int v){
35 a[++tote].v=v;
36 a[tote].next=head[u];
37 head[u]=tote;
38 }
39 void updata(int &u,int l,int r,int x){
40 if(!u)u=++cnt;
41 if(l==r){
42 t[u].v.v++;
43 t[u].v.id=x;
44 return;
45 }
46 int mid=(l+r)/2;
47 if(x<=mid)updata(t[u].ls,l,mid,x);
48 else updata(t[u].rs,mid+1,r,x);
49 t[u].v=max(t[t[u].ls].v,t[t[u].rs].v);
50 }
51 void merge(int &x,int y){
52 if(!x||!y){
53 x|=y;
54 return;
55 }
56 if(!t[x].ls&&!t[x].rs){
57 t[x].v.v+=t[y].v.v;
58 return;
59 }
60 merge(t[x].ls,t[y].ls);
61 merge(t[x].rs,t[y].rs);
62 t[x].v=max(t[t[x].ls].v,t[t[x].rs].v);
63 }
64 task query(int u,int l,int r,int L,int R){
65 if(L<=l&&r<=R){
66 return t[u].v;
67 }
68 int mid=(l+r)/2;
69 task ret;
70 if(L<=mid)ret=max(ret,query(t[u].ls,l,mid,L,R));
71 if(mid<R)ret=max(ret,query(t[u].rs,mid+1,r,L,R));
72 return ret;
73 }
74 void extend(int ch){
75 int p=last,np=++tot;
76 mx[np]=mx[p]+1;
77 for(;p&&!son[p][ch];p=fa[p])son[p][ch]=np;
78 if(!p)fa[np]=rt;
79 else{
80 int q=son[p][ch];
81 if(mx[q]==mx[p]+1)fa[np]=q;
82 else{
83 int nq=++tot;
84 mx[nq]=mx[p]+1;
85 memcpy(son[nq],son[q],sizeof(son[q]));
86 fa[nq]=fa[q];
87 fa[q]=fa[np]=nq;
88 for(;p&&son[p][ch]==q;p=fa[p])son[p][ch]=nq;
89 }
90 }
91 last=np;
92 }
93 void dfs(int u){
94 for(int tmp=head[u];tmp!=-1;tmp=a[tmp].next){
95 int v=a[tmp].v;
96 dfs(v);
97 merge(rts[u],rts[v]);
98 }
99 for(int i=0,ii=as[u].size();i<ii;i++){
100 ans[as[u][i]]=query(rts[u],1,m,q[as[u][i]].l,q[as[u][i]].r);
101 }
102 }
103 int main(){
104 memset(head,-1,sizeof(head));
105 scanf("%s%d",st+1,&m);
106 n=strlen(st+1);
107 for(int i=1;i<=m;i++){
108 scanf("%s",tt);
109 len=strlen(tt);
110 last=rt;
111 for(int j=0;j<len;j++){
112 extend(tt[j]-'a');
113 updata(rts[last],1,m,i);
114 }
115 }
116 scanf("%d",&Q);
117 for(int i=1;i<=Q;i++){
118 scanf("%d%d%d%d",&q[i].l,&q[i].r,&q[i].ql,&q[i].qr);
119 qrs[q[i].qr].push_back(i);
120 }
121 for(int i=2;i<=tot;i++){
122 f[i][0]=fa[i];
123 add(fa[i],i);
124 }
125 for(int j=1;j<=19;j++){
126 for(int i=1;i<=tot;i++){
127 f[i][j]=f[f[i][j-1]][j-1];
128 }
129 }
130 len=0;
131 for(int i=1;i<=n;i++){
132 ch=st[i]-'a';
133 for(;nw&&!son[nw][ch];)nw=fa[nw],len=mx[nw];
134 if(!nw){
135 nw=rt;
136 len=0;
137 }else{
138 nw=son[nw][ch];
139 len++;
140 for(int j=0,jj=qrs[i].size();j<jj;j++){
141 int v=qrs[i][j];
142 if(len>=q[v].qr-q[v].ql+1){
143 int _nw=nw;
144 for(int k=19;k>=0;k--){
145 if(mx[f[_nw][k]]>=q[v].qr-q[v].ql+1){
146 _nw=f[_nw][k];
147 }
148 }
149 as[_nw].push_back(v);
150 }
151 }
152 }
153 }
154 dfs(1);
155 for(int i=1;i<=Q;i++){
156 if(!ans[i].v)ans[i].id=q[i].l;
157 printf("%d %d\n",ans[i].id,ans[i].v);
158 }
159 return 0;
160 }