题目链接
\(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;
}