CF666E Forensic Examination(後綴自動機+線段樹合並)
阿新 • • 發佈:2019-01-06
char cst d+ max getchar() 大於 size tin ||
給你一個串S以及一個字符串數組T[1..m],q次詢問,每次問S的子串S[pl..pr]在T[l..r]中的哪個串裏的出現次數最多,並輸出出現次數。
如有多解輸出最靠前的那一個。
我們首先對m個字符串數組建出後綴自動機,然後我們可以通過跳trans邊找到S前i個字符代表的前綴的最長後綴。我們要找的是S[pl..pr]並不是以pr結束最長的後綴,但我們可以確定S[pl..pr]一定是當前點的祖先所以當我們跳到pr代表的點時我們倍增往上跳知道找到一個點的長度剛好大於等於pr-pl+1,這個點就是詢問區間代表的點。
那麽我們怎麽求答案呢?上線段樹合並就行(線段樹以[1,m]為值域),這就要求我們對詢問離線。
#include<iostream> #include<cstring> #include<cstdio> #include<cmath> #include<algorithm> #include<vector> using namespace std; const int N=501000; int n,m,L; char S[N],s[N]; int cnt,head[N]; struct edge{ int to,nxt; }e[N]; void add(int u,int v){ cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } struct ques{ int l,a,b,id; ques(int ll=0,int aa=0,int bb=0,int idx=0){ l=ll;a=aa;b=bb;id=idx; } }; struct data{ int mx,id; data(int mxx=0,int idx=0){ mx=mxx;id=idx; } }ans[N]; struct qu{ int a,b,id; qu(int idx=0,int aa=0,int bb=0){ a=aa;b=bb;id=idx; } }; vector<qu> que[N]; data max(data a,data b){ if(a.mx==b.mx){ if(a.id<b.id)return a; return b; } if(a.mx>b.mx)return a; return b; } vector<ques> vec[N]; struct sam{ int tot,u,trans[N][27],fa[N][23],len[N]; int root[N],ch[N*50][2],cnt,mx[N*50],id[N*50]; void init(){tot=u=1;} void rebuild(){u=1;} void ins(int k,int c){ if(trans[u][c]){ int v=trans[u][c]; if(len[v]==len[u]+1)u=v,add(1,m,k,root[v]); else{ int x=++tot;add(1,m,k,root[x]);len[x]=len[u]+1; memcpy(trans[x],trans[v],sizeof(trans[v])); fa[x][0]=fa[v][0];fa[v][0]=x; for(;u&&trans[u][c]==v;u=fa[u][0])trans[u][c]=x; u=x; } } else{ int x=++tot;add(1,m,k,root[x]);len[x]=len[u]+1; for(;u&&trans[u][c]==0;u=fa[u][0])trans[u][c]=x; if(u==0)fa[x][0]=1; else{ int v=trans[u][c]; if(len[v]==len[u]+1)fa[x][0]=v; else{ int w=++tot; len[w]=len[u]+1; fa[w][0]=fa[v][0]; memcpy(trans[w],trans[v],sizeof(trans[w])); fa[x][0]=fa[v][0]=w; for(;u&&trans[u][c]==v;u=fa[u][0])trans[u][c]=w; } } u=x; } } void update(int now){ if(mx[ch[now][0]]>=mx[ch[now][1]])mx[now]=mx[ch[now][0]],id[now]=id[ch[now][0]]; else mx[now]=mx[ch[now][1]],id[now]=id[ch[now][1]]; } void add(int l,int r,int x,int &now){ if(now==0)now=++cnt; if(l==r){mx[now]++;id[now]=l;return;} int mid=(l+r)>>1; if(x>mid)add(mid+1,r,x,ch[now][1]); else add(l,mid,x,ch[now][0]); update(now); } data check(int l,int r,int L,int R,int now){ if(l==L&&r==R)return (data(mx[now],id[now])); int mid=(l+r)>>1; if(L>mid)return check(mid+1,r,L,R,ch[now][1]); else if(R<=mid)return check(l,mid,L,R,ch[now][0]); else return max(check(l,mid,L,mid,ch[now][0]),check(mid+1,r,mid+1,R,ch[now][1])); } void merge(int l,int r,int &x,int y){ if(!x||!y){x=x|y;return;} if(l==r){mx[x]+=mx[y];return;} int mid=(l+r)>>1; merge(l,mid,ch[x][0],ch[y][0]); merge(mid+1,r,ch[x][1],ch[y][1]); update(x); } void dfs(int u){ for(int i=head[u];i;i=e[i].nxt){ int v=e[i].to; dfs(v); merge(1,m,root[u],root[v]); } for(int i=0;i<que[u].size();i++){ data a=check(1,m,que[u][i].a,que[u][i].b,root[u]); if(a.mx==0)continue; ans[que[u][i].id]=a; } } void work(){ int now=1; int lon=0; for(int i=1;i<=L;i++){ while(trans[now][S[i]-'a'+1]==0&&now)now=fa[now][0],lon=len[now]; if(now==0){ for(int j=0;j<vec[i].size();j++)ans[vec[i][j].id].id=vec[i][j].a; now=1;lon=0;continue; } now=trans[now][S[i]-'a'+1];lon++; for(int j=0;j<vec[i].size();j++){ ans[vec[i][j].id].id=vec[i][j].a; if(lon<i-vec[i][j].l+1)continue; int x=now; for(int k=20;k>=0;k--) if(len[fa[x][k]]>=i-vec[i][j].l+1)x=fa[x][k]; qu a=qu(vec[i][j].id,vec[i][j].a,vec[i][j].b); que[x].push_back(a); } } } }sam; int read(){ int sum=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();} return sum*f; } int main(){ scanf("%s",S+1); scanf("%d",&m); sam.init(); for(int i=1;i<=m;i++){ scanf("%s",s+1); int len=strlen(s+1); for(int j=1;j<=len;j++)sam.ins(i,s[j]-'a'+1); sam.rebuild(); } for(int i=2;i<=sam.tot;i++)add(sam.fa[i][0],i); for(int j=1;j<=20;j++) for(int i=2;i<=sam.tot;i++) sam.fa[i][j]=sam.fa[sam.fa[i][j-1]][j-1]; scanf("%d",&n); for(int i=1;i<=n;i++){ int c=read(),d=read(),a=read(),b=read(); vec[b].push_back(ques(a,c,d,i)); } L=strlen(S+1); sam.work(); sam.dfs(1); for(int i=1;i<=n;i++)printf("%d %d\n",ans[i].id,ans[i].mx); return 0; }
CF666E Forensic Examination(後綴自動機+線段樹合並)