洛谷题目传送门
我们先对S和所有的询问串共同建立一个广义SAM
并且在每个节点上维护一颗线段树代表这个节点代表的字串在各个串中出现次数
也就是题目要求的,那么这个显然可以线段树合并维护出来
然后询问的时候,我们要先在SAM上定位这个区间的字串,这个也很简单:每个子串可以看成一个前缀的后缀,而我们知道跳parent边就是不断取后缀的过程,所以我们先找到这个区间的前缀在SAM上的节点,然后跳parent边一直到合法,这个过程可以用书树上倍增加速
然后我们在这个节点对应的线段树上查询题目给的区间内出现次数最多的串是哪个即可
#include<bits/stdc++.h>
using namespace std;
#define PII pair<int,int>
#define mk(x,y) make_pair(x,y)
#define X(x) x.first
#define Y(x) x.second
const int S = 6e5+7;
const int N = 2*S;
int tr[N][26],len[N],fa[N];
int tot=0;
int n,m;
int sum=0;
void copy(int x,int y)
{
len[x]=len[y];
fa[x]=fa[y];
for(int c=0;c<26;c++)
tr[x][c]=tr[y][c];
}
int nodes[S];
int Extend(int c,int last)
{
int p=last,np=++tot;
if(tot>=2e6)
{
cout<<sum;
exit(0);
}
len[np]=len[p]+1;
while(p&&!tr[p][c])
{
tr[p][c]=np;
p=fa[p];
}
if(!p) fa[np]=1;
else
{
int q=tr[p][c];
if(len[q]==len[p]+1) fa[np]=q;
else
{
int nq=++tot;
copy(nq,q);
len[nq]=len[p]+1;
fa[np]=fa[q]=nq;
while(p&&tr[p][c]==q)
{
tr[p][c]=nq;
p=fa[p];
}
}
}
return np;
}
int trie[S][26];
char s[S];
vector<int> appear[S];
bool ins[S];
int length;
void Insert(int x)
{
int p=0;
ins[p]=1;
length=strlen(s+1);
for(int i=1;i<=length;i++)
{
int c=s[i]-'a';
if(!trie[p][c]) trie[p][c]=++tot;
p=trie[p][c];
if(x!=0) appear[p].push_back(x);
else ins[p]=1;
}
}
int num=0;
int lson[N*20],rson[N*20],rot[N];
int Max[N*20],Pos[N*20];
void pushup(int k)
{
if(Max[lson[k]]>Max[rson[k]])
{
Max[k]=Max[lson[k]];
Pos[k]=Pos[lson[k]];
}
else if(Max[lson[k]]<Max[rson[k]])
{
Max[k]=Max[rson[k]];
Pos[k]=Pos[rson[k]];
}
else
{
Max[k]=Max[lson[k]];
Pos[k]=Pos[lson[k]];
}
}
void Modify(int &k,int l,int r,int pos)
{
if(!k) k=++num;
if(l==r)
{
Max[k]++;
Pos[k]=l;
return;
}
int mid=(l+r)>>1;
if(pos<=mid) Modify(lson[k],l,mid,pos);
else Modify(rson[k],mid+1,r,pos);
pushup(k);
}
int Merge(int x,int y,int l,int r)
{
if(!x||!y) return x+y;
int k=++num;
if(l==r)
{
Max[k]=Max[x]+Max[y];
Pos[k]=l;
return k;
}
int mid=(l+r)>>1;
lson[k]=Merge(lson[x],lson[y],l,mid);
rson[k]=Merge(rson[x],rson[y],mid+1,r);
pushup(k);
return k;
}
void Better(PII &A,PII B)
{
if(Y(A)<Y(B)||(Y(A)==Y(B)&&X(A)>X(B))) A=B;
}
PII Ask(int k,int l,int r,int L,int R)
{
if(!k) return mk(0,0);
if(L<=l&&r<=R) return mk(Pos[k],Max[k]);
int mid=(l+r)>>1;
PII res=mk(0,0);
if(L<=mid) Better(res,Ask(lson[k],l,mid,L,R));
if(R>mid) Better(res,Ask(rson[k],mid+1,r,L,R));
return res;
}
struct edge
{
int y,next;
}e[N];
int link[N],t=0;
int pos[S],pre[S];
void add(int x,int y)
{
e[++t].y=y;
e[t].next=link[x];
link[x]=t;
}
queue<int> q;
int f[N][30];
int tt=0;
void dfs(int x)
{
for(int i=link[x];i;i=e[i].next)
{
int y=e[i].y;
f[y][0]=x;
for(int k=1;k<=tt;k++)
f[y][k]=f[f[y][k-1]][k-1];
dfs(y);
rot[x]=Merge(rot[x],rot[y],1,m);
}
}
void Build()
{
sum=tot;
tot=0;
nodes[0]=++tot;
q.push(0);
while(!q.empty())
{
int x=q.front();
q.pop();
for(int c=0;c<26;c++)
{
if(!trie[x][c]) continue;
int y=trie[x][c];
nodes[y]=Extend(c,nodes[x]);
for(int p=0;p<(int)appear[y].size();p++)
Modify(rot[nodes[y]],1,m,appear[y][p]);
if(ins[x]&&ins[y])
{
pre[y]=pre[x]+1;
pos[pre[y]]=nodes[y];
}
q.push(y);
}
}
for(int i=2;i<=tot;i++)
add(fa[i],i);
tt=log2(tot+1);
dfs(1);
}
inline int read()
{
int X=0;bool flag=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')flag=0;ch=getchar();}
while(ch>='0'&&ch<='9'){X=(X<<1)+(X<<3)+ch-'0';ch=getchar();}
if(flag) return X;
return ~(X-1);
}
int Find(int x,int l)
{
int r=x;
for(int k=tt;k>=0;k--)
{
if(f[x][k]&&len[r]-len[f[x][k]]<=l)
{
x=f[x][k];
}
}
return x;
}
int main()
{
scanf("%s",s+1);
Insert(0);
m=read();
for(int i=1;i<=m;i++)
{
scanf("%s",s+1);
Insert(i);
}
Build();
int q=read();
while(q--)
{
int l=read(),r=read(),ql=read(),qr=read();
ql--;
int x=Find(pos[qr],ql);
PII ans=Ask(rot[x],1,m,l,r);
if(X(ans)==0) X(ans)=l;
printf("%d %d\n",X(ans),Y(ans));
}
return 0;
}