题意
今天发生了一个叫nofind的蒟蒻因为while写成if调了一下午的故事。
首先考虑这两个限制:
1.(S[sl...sr])这个子串。
2.在(T[ql..qr])这些字符串中。
我们先对所有的(T_i)建出一个广义后缀自动机,不会广义后缀自动机的出门左转这道题。
先考虑第二个限制:
之后我们对(SAM)上的每个点用一个动态开点线段树维护答案,线段树以([1,m])为下标,叶子结点([l,l])放的是该节点在(T_l)中出现的次数,之后我们向上合并并且维护最大值的位置,这个可以用经典的后缀自动机+线段树合并的套路做。
在实现时我用了(STL)的(pair),第一关键字是出现次数,第二关键字是位置,同时第二关键字取了负数,这样可以直接两个(pair)取(max)。
再考虑第一个限制:
我们只要找到子串(S[sl...sr])所在的节点,之后线段树上直接查询即可。那么接下来就要找到(S[sl...sr])所在节点。
我们可以先找到(S[1...sr]),之后倍增跳到最后一个一个点(x),(x)满足(len_xgeqslant (sr-sl+1)),那么(x)即为所求。
但是显然我们不能每次询问都暴力找,于是我们将询问离线,对(S)的每个位置开一个(vector),将询问((sl,sr))放在(sr)的(vector)中。之后在(SAM)上对(S)进行匹配,到了第(i)个位置时,所有以(i)为结尾的子串必定是当前节点的祖先,我们这时倍增处理所有以(i)为结尾的询问即可。
code:
#include<bits/stdc++.h>
using namespace std;
#define pii pair<int,int>
#define mkp make_pair
#define fir first
#define sec second
const int maxn=1e6+10;
const int maxQ=5*1e5+10;
int n,m,Q,cnt_edge,tot,lg;
int head[maxn],root[maxn];
int f[maxn][18];
char s[maxn],t[maxn];
pii ans[maxQ];
vector<int>qr_id[maxn];
struct edge{int to,nxt;}e[maxn];
inline void add_edge(int u,int v)
{
e[++cnt_edge].nxt=head[u];
head[u]=cnt_edge;
e[cnt_edge].to=v;
}
struct Query{int l1,r1,l2,r2,id;}qr[maxQ];
struct Seg
{
#define lc(p) seg[p].lc
#define rc(p) seg[p].rc
#define data(p) seg[p].data
int lc,rc;
pii data;
}seg[maxn*35];
inline void up(int p){data(p)=max(data(lc(p)),data(rc(p)));}
void add(int& p,int l,int r,int pos)
{
if(!p)p=++tot;
if(l==r){data(p).fir++,data(p).sec=-l;return;}
int mid=(l+r)>>1;
if(pos<=mid)add(lc(p),l,mid,pos);
else add(rc(p),mid+1,r,pos);
up(p);
}
int merge(int p,int q,int l,int r)
{
if(!p||!q)
{
int x=++tot;
seg[x]=seg[p+q];
return x;
}
int mid=(l+r)>>1,x=++tot;
if(l==r){data(x).fir=data(p).fir+data(q).fir;data(x).sec=-l;return x;}
lc(x)=merge(lc(p),lc(q),l,mid);
rc(x)=merge(rc(p),rc(q),mid+1,r);
up(x);
return x;
}
pii query(int p,int l,int r,int ql,int qr)
{
if(!p)return mkp(0,-ql);
if(l>=ql&&r<=qr)return data(p);
int mid=(l+r)>>1;pii res=mkp(0,-ql);
if(ql<=mid)res=max(res,query(lc(p),l,mid,ql,qr));
if(qr>mid)res=max(res,query(rc(p),mid+1,r,ql,qr));
return res;
}
struct SAM
{
int last,tot;
int fa[maxn],len[maxn];
int ch[maxn][26];
SAM(){last=tot=1;}
inline void add(int c)
{
if(ch[last][c]&&len[ch[last][c]]==len[last]+1){last=ch[last][c];return;}
int now=++tot;len[now]=len[last]+1;
int p=last;
while(p&&!ch[p][c])ch[p][c]=now,p=fa[p];
if(!p){fa[now]=1;last=now;return;}
int q=ch[p][c];
bool flag=0;
if(len[q]==len[p]+1)fa[now]=q;
else
{
if(p==last)flag=1;
int nowq=++tot;len[nowq]=len[p]+1;
memcpy(ch[nowq],ch[q],sizeof(ch[q]));
while(p&&ch[p][c]==q)ch[p][c]=nowq,p=fa[p];
fa[nowq]=fa[q],fa[q]=fa[now]=nowq;
if(flag)last=nowq;
}
if(!flag)last=now;
}
}sam;
void dfs(int x)
{
for(int i=1;i<=lg;i++)f[x][i]=f[f[x][i-1]][i-1];
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
f[y][0]=x;dfs(y);
root[x]=merge(root[x],root[y],1,m);
}
}
inline int find(int x,int len)
{
for(int i=lg;~i;i--)if(f[x][i]&&sam.len[f[x][i]]>=len)x=f[x][i];
return x;
}
inline void solve()
{
int now=1,nowl=0;
for(int i=1;i<=n;i++)
{
int c=s[i]-'a';
while(now&&!sam.ch[now][c])now=sam.fa[now],nowl=sam.len[now];
if(!now){now=1;nowl=0;continue;}
now=sam.ch[now][c];nowl++;
for(unsigned int j=0;j<qr_id[i].size();j++)
{
int id=qr_id[i][j];
if(qr[id].r1-qr[id].l1+1>nowl)continue;
int tmp=find(now,i-qr[id].l1+1);
ans[id]=query(root[tmp],1,m,qr[id].l2,qr[id].r2);
}
}
}
int main()
{
//freopen("test.in","r",stdin);
//freopen("test.out","w",stdout);
scanf("%s",s+1);n=strlen(s+1);
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
sam.last=1;
scanf("%s",t+1);
int len=strlen(t+1);
for(int j=1;j<=len;j++)sam.add(t[j]-'a'),add(root[sam.last],1,m,i);
}
scanf("%d",&Q);
for(int i=1;i<=Q;i++)scanf("%d%d%d%d",&qr[i].l2,&qr[i].r2,&qr[i].l1,&qr[i].r1);
for(int i=1;i<=Q;i++)qr_id[qr[i].r1].push_back(i);
for(int i=2;i<=sam.tot;i++)add_edge(sam.fa[i],i);
lg=(int)log2(sam.tot)+1;dfs(1);
solve();
for(int i=1;i<=Q;i++)if(!ans[i].fir)ans[i].sec=-qr[i].l2;
for(int i=1;i<=Q;i++)printf("%d %d
",-ans[i].sec,ans[i].fir);
return 0;
}