日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Codeforces.666E.Forensic Examination(广义后缀自动机 线段树合并)

發布時間:2023/12/2 编程问答 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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|\)狀態??梢杂帽对鰧崿F。
(就是從\(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(广义后缀自动机 线段树合并)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。