题解:
广义$SAM$+倍增+线段树合并三连。
对于给出的$m$个串建广义后缀自动机,用线段树合并处理$parent$树子树内所有串出现次数。
将询问挂在第$pr$位上。
最后扫一遍原串,记录当前节点以及当前串长,在$parent$树上倍增找$s[pl,pr]$串,最后用线段树查询就好了。
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N = 500050; typedef long long ll; template<typename T> inline void read(T&x) { T f = 1,c = 0;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){c=c*10+ch-'0';ch=getchar();} x = f*c; } char s[N],s0[N]; int m,rt[N<<1],q,a1[N],a2[N]; struct segtree { int tot,ls[N<<4],rs[N<<4],siz[N<<4],nam[N<<4]; void update(int u) { if(siz[ls[u]]<siz[rs[u]]) { nam[u] = nam[rs[u]]; siz[u] = siz[rs[u]]; }else { nam[u] = nam[ls[u]]; siz[u] = siz[ls[u]]; } } void insert(int l,int r,int &u,int qx) { if(!u)u=++tot; if(l==r) { nam[u] = l; siz[u]++; return ; } int mid = (l+r)>>1; if(qx<=mid)insert(l,mid,ls[u],qx); else insert(mid+1,r,rs[u],qx); update(u); } int merge(int l,int r,int x,int y) { if(!x||!y)return x+y; int z = ++tot,mid = (l+r)>>1; if(l==r)siz[z] = siz[x]+siz[y],nam[z] = l; else { ls[z] = merge(l,mid,ls[x],ls[y]); rs[z] = merge(mid+1,r,rs[x],rs[y]); update(z); } return z; } void query(int l,int r,int u,int ql,int qr,int&h1,int&h2) { if(!u)return ; if(l==ql&&r==qr) { if(h2<siz[u]) { h1 = nam[u]; h2 = siz[u]; } return ; } int mid = (l+r)>>1; if(qr<=mid)query(l,mid,ls[u],ql,qr,h1,h2); else if(ql>mid)query(mid+1,r,rs[u],ql,qr,h1,h2); else query(l,mid,ls[u],ql,mid,h1,h2),query(mid+1,r,rs[u],mid+1,qr,h1,h2); } }tr; int f[N<<1][22]; struct Pnt { int ch[26],len,pre; }p[N<<1]; struct SAM { int tot,las; SAM(){tot=las=1;} void init(){las=1;} void insert(int c,int i) { if(p[las].ch[c]) { int lp = las,lq = p[lp].ch[c]; if(p[lq].len==p[lp].len+1) { las = lq; tr.insert(1,m,rt[lq],i); }else { int nq = ++tot; p[nq] = p[lq]; p[nq].len = p[lp].len+1; p[lq].pre = nq; for(;p[lp].ch[c]==lq;lp=p[lp].pre) { p[lp].ch[c] = nq; } tr.insert(1,m,rt[nq],i); las = nq; } return ; } int np,nq,lp,lq; np = ++tot; p[np].len = p[las].len+1; tr.insert(1,m,rt[np],i); for(lp=las;lp&&!p[lp].ch[c];lp=p[lp].pre) p[lp].ch[c]=np; if(!lp)p[np].pre = 1; else { lq = p[lp].ch[c]; if(p[lq].len==p[lp].len+1)p[np].pre = lq; else { nq = ++tot; p[nq] = p[lq]; p[nq].len = p[lp].len+1; p[np].pre = p[lq].pre = nq; for(;p[lp].ch[c]==lq;lp=p[lp].pre) p[lp].ch[c]=nq; } } las = np; } int hs[N<<1],sta[N<<1]; void build() { for(int i=1;i<=tot;i++)hs[p[i].len]++,f[i][0]=p[i].pre; for(int i=1;i<=tot;i++)hs[i]+=hs[i-1]; for(int i=1;i<=tot;i++)sta[hs[p[i].len]--]=i; for(int i=tot;i>=1;i--) rt[p[sta[i]].pre]=tr.merge(1,m,rt[p[sta[i]].pre],rt[sta[i]]); for(int k=1;k<=20;k++) for(int i=1;i<=tot;i++) f[i][k]=f[f[i][k-1]][k-1]; } }sam; int hed[N],cnt; struct EG { int l,r,pl,id,nxt; }e[N]; void ae(int f,int l,int r,int i,int pl) { e[++cnt].l = l; e[cnt].r = r; e[cnt].id = i; e[cnt].pl = pl; e[cnt].nxt = hed[f]; hed[f] = cnt; } int main() { // freopen("tt.in","r",stdin); scanf("%s",s+1); read(m); for(int i=1;i<=m;i++) { scanf("%s",s0+1); int len = strlen(s0+1); sam.init(); for(int j=1;j<=len;j++) { int c = s0[j]-'a'; sam.insert(c,i); } } sam.build(); read(q); for(int l,r,pl,pr,i=1;i<=q;i++) { read(l),read(r),read(pl),read(pr); ae(pr,l,r,i,pl); } int u = 1; for(int i=1,lim=strlen(s+1),len=0;i<=lim;i++) { while(u&&!p[u].ch[s[i]-'a'])u=p[u].pre,len=p[u].len; if(!u)u=1,len=0; else u=p[u].ch[s[i]-'a'],len++; for(int j=hed[i];j;j=e[j].nxt) { int now = u; int ln = i-e[j].pl+1; if(len<ln) { a1[e[j].id] = e[j].l; continue; } for(int k=20;k>=0;k--) if(p[f[now][k]].len>=ln)now=f[now][k]; tr.query(1,m,rt[now],e[j].l,e[j].r,a1[e[j].id]=e[j].l,a2[e[j].id]); } } for(int i=1;i<=q;i++) printf("%d %d ",a1[i],a2[i]); return 0; }