Codeforces.666E.Forensic Examination(广义后缀自动机 线段树合并)
題目鏈接
\(Description\)
給定串\(S\)和\(m\)個串\(T_i\)。\(Q\)次詢問,每次詢問\(l,r,p_l,p_r\),求\(S[p_l\sim p_r]\)在\(T_l\sim T_r\)中的哪個串出現次數最多,輸出最多次數以及它是\(T\)中的第幾個。若最多的有多個,輸出下標最小的。
\(Solution\)
挺好的題吧
對\(T\)個串建SAM,然后要求出SAM每個節點上\(|right|\)最大的是哪個串。
每個節點的\(|right|\)可以在DFS parent樹時合并子節點得到。如果用線段樹,區間\(|right|\)最大的是哪個串也可以維護出來。
那么可以離線,在每個點處處理該點上的詢問,邊DFS邊合并線段樹得到所有答案。(當然可持久化一下在線也行?)
怎么得到\(S[p_l\sim p_r]\)在SAM上的匹配節點呢?
維護一個節點指針\(p\),拿\(S\)在SAM上盡可能匹配,匹配不了就跳\(fa\)。這樣能保證當前\(S\)的后綴(\(S[i]\))一定在\(p\)節點出現了。
所以在\(i\)這里處理\(p_r=i\)的詢問。只要我們從當前節點\(p\)一直跳\(fa\),就能找到\(S[p_l,p_r]\)所在的節點,其答案就是該節點的\(|right|\)狀態。可以用倍增實現。
(就是從\(S[1,p_r]\)所匹配的節點\(p\)往上跳,跳到從上往下第一個\(len_x\geq p_r-p_l+1\)的節點\(x\),\(x\)就是\(S[p_l,p_r]\)所在的節點)
如果出現次數為\(0\)的話也要輸出最靠前的(即\(l\))。=-=
唉 6點多寫完代碼調到現在 心累
//529ms 56700KB #include <cstdio> #include <cctype> #include <cstring> #include <algorithm> #define gc() getchar() #define Bit 16 const int N=5e5+5,M=5e4+5,S=M<<1;int m,root[S],fa[S][18]; char s[N],tmp[M]; struct Edge {int Enum,H[N],nxt[N],to[N];inline void AddEdge(int u,int v){to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;} }Pos,Qy; struct Edge2 {int Enum,H[S],nxt[S],to[S];inline void AddEdge(int u,int v){to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;} }Par; struct Queries{int l,r,pl,pr; }q[N]; struct Suffix_Automaton {int tot,las,fa[S],son[S][26],len[S];Suffix_Automaton() {tot=las=1;}void Insert(int c){int np=++tot,p=las; len[las=np]=len[p]+1;for(; p&&!son[p][c]; p=fa[p]) son[p][c]=np;if(!p) fa[np]=1;else{int q=son[p][c];if(len[q]==len[p]+1) fa[np]=q;else{int nq=++tot; len[nq]=len[p]+1;memcpy(son[nq],son[q],sizeof son[q]);fa[nq]=fa[q], fa[q]=fa[np]=nq;for(; son[p][c]==q; p=fa[p]) son[p][c]=nq;}}} }sam; struct Node {int val,id;bool operator <(const Node &x)const{return val<x.val||(val==x.val&&id>x.id);} }Ans[N]; struct Segment_Tree {#define S M*17#define lson son[x][0]#define rson son[x][1]int tot,son[S][2];Node node[S];#undef S#define Update(x) node[x]=std::max(node[lson],node[rson]);void Insert(int &x,int l,int r,int pos){x=++tot;if(l==r) return (void)(node[x]=(Node){1,pos});int m=l+r>>1;pos<=m ? Insert(lson,l,m,pos) : Insert(rson,m+1,r,pos);Update(x);//Update }int Merge(int x,int y){if(!x||!y) return x^y;if(!lson&&!rson) return node[x].val+=node[y].val, x;//葉節點,合并right lson=Merge(lson,son[y][0]), rson=Merge(rson,son[y][1]);Update(x); return x;}Node Query(int x,int l,int r,int L,int R){if(!x) return (Node){0,L};if(L<=l && r<=R) return node[x];int m=l+r>>1;if(L<=m)if(m<R) return std::max(Query(lson,l,m,L,R),Query(rson,m+1,r,L,R));else return Query(lson,l,m,L,R);return Query(rson,m+1,r,L,R);} }T;inline int read() {int now=0;register char c=gc();for(;!isdigit(c);c=gc());for(;isdigit(c);now=now*10+c-'0',c=gc());return now; } void DFS(int x) {for(int i=Par.H[x]; i; i=Par.nxt[i])DFS(Par.to[i]), root[x]=T.Merge(root[x],root[Par.to[i]]);for(int i=Pos.H[x],id; i; i=Pos.nxt[i])id=Pos.to[i], Ans[id]=T.Query(root[x],1,m,q[id].l,q[id].r); }int main() {scanf("%s",s+1); int n=strlen(s+1);m=read();for(int i=1; i<=m; ++i){scanf("%s",tmp), sam.las=1;for(int j=0,l=strlen(tmp); j<l; ++j)sam.Insert(tmp[j]-'a'), T.Insert(root[sam.las],1,m,i);//不就是每位的|right[las]|=1嗎→_→我在糾結什么 //就是las節點的線段樹上,i位置的|right|=1,給i位置+1}int Q=read();for(int i=1; i<=Q; ++i)q[i]=(Queries){read(),read(),read(),read()}, Qy.AddEdge(q[i].pr,i);int lim=sam.tot;for(int x=2; x<=lim; ++x) Par.AddEdge(fa[x][0]=sam.fa[x],x);for(int i=1; i<=Bit; ++i)for(int x=2; x<=lim; ++x)fa[x][i]=fa[fa[x][i-1]][i-1];for(int c,now=0,p=1,i=1; i<=n; ++i){if(sam.son[p][c=s[i]-'a']) p=sam.son[p][c], ++now;//!!!靠 這寫錯調了兩個小時 唉 心累 else{for(c=s[i]-'a'; p&&!sam.son[p][c]; p=sam.fa[p]);if(!p) {p=1, now=0; continue;}now=sam.len[p]+1, p=sam.son[p][c];}for(int j=Qy.H[i],len,id; j; j=Qy.nxt[j]){id=Qy.to[j];if(now<(len=q[id].pr-q[id].pl+1)) continue;int x=p;for(int i=Bit; ~i; --i)if(sam.len[fa[x][i]]>=len) x=fa[x][i];Pos.AddEdge(x,id);}}DFS(1);for(int i=1; i<=Q; ++i)if(!Ans[i].val) printf("%d 0\n",q[i].l);else printf("%d %d\n",Ans[i].id,Ans[i].val);return 0; }轉載于:https://www.cnblogs.com/SovietPower/p/9368392.html
總結
以上是生活随笔為你收集整理的Codeforces.666E.Forensic Examination(广义后缀自动机 线段树合并)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: js中call和apply的作用和用法
- 下一篇: linux内核工作队列怎么工作的?