题意:
给定一个字符串 S S S, q q q 次询问 T , l , r T,l,r T,l,r,求 T T T 和 S [ l , r ] S[l,r] S[l,r] 的本质不同公共子串数目。
∣ S ∣ ≤ 5 × 1 0 5 |S|\leq 5\times 10^5 ∣S∣≤5×105, q ≤ 1 0 5 q\leq 10^5 q≤105, ∑ ∣ T ∣ ≤ 1 0 6 \sum |T|\leq 10^6 ∑∣T∣≤106。
题解:
首先看一个弱化版的问题:给出两个串 S , T S,T S,T,求 S , T S,T S,T 的本质不同公共子串数目。
由于要求本质不同,所以思路是建出 T T T 的后缀自动机,然后看每一个节点所对应的那一些串中有多少是 S S S 的子串,即这个节点中长度小于等于多少的串都是 S S S 的子串。
为了求出这个,我们需要求出 T T T 的每一个前缀的最长后缀使得它是 S S S 的子串,也就是 T T T 在 S S S 的后缀自动机上匹配的过程:每次加入一个字符 c c c,不断跳 f a fa fa 直到当前节点存在 c c c 的出边为止。
时间复杂度 O ( ∣ S ∣ + ∣ T ∣ ) O(|S|+|T|) O(∣S∣+∣T∣),此处看作后缀自动机的构造为线性。
但现在询问的是 S [ l , r ] S[l,r] S[l,r] 和 T T T 的本质不同公共子串数目。我们考虑仍然使用这个思路,不过要做些许修改。
要做修改的地方是 T T T 在 S S S 后缀自动机上匹配的过程,我们求出了 T T T 当前前缀的最长后缀使得它是 S S S 的子串后,可能这个后缀并不是 S [ l , r ] S[l,r] S[l,r] 的子串。那么应该继续往上跳,直到跳到一个点 u u u 使得 u u u 代表的所有串中有一个串出现在 S [ l , r ] S[l,r] S[l,r] 内了。也就是说存在一个 p ∈ endpos ( u ) p\in\operatorname{endpos}(u) p∈endpos(u) 使得 l ≤ p − l e n ( f a ( p ) ) ∧ p ≤ r l\leq p-len(fa(p))\land p\leq r l≤p−len(fa(p))∧p≤r。相当于说我们要找到 endpos ( u ) \operatorname{endpos}(u) endpos(u) 里面小于等于 r r r 的最大的元素并 check 是否满足上面的条件。
那么我们就需要维护一个节点的 endpos \operatorname{endpos} endpos 集合,使用线段树合并即可,需要可持久化。时空复杂度 O ( n log n ) O(n\log n) O(nlogn)。
#include<bits/stdc++.h>
#define N 500010
#define ll long long
using namespace std;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^'0');
ch=getchar();
}
return x*f;
}
int n,m,q,L,R,lsp[N];
char s[N],t[N];
namespace SAMS
{
namespace Seg
{
#define lc(u) ch[u][0]
#define rc(u) ch[u][1]
const int NN=20000000;
int node,ch[NN][2],size[NN];
int copy(int u)
{
++node,lc(node)=lc(u),rc(node)=rc(u),size[node]=size[u];
return node;
}
void up(int u){size[u]=size[lc(u)]+size[rc(u)];}
void update(int &u,int l,int r,int x)
{
if(!u) u=++node;
if(l==r)
{
size[u]++;
return;
}
int mid=(l+r)>>1;
if(x<=mid) update(lc(u),l,mid,x);
else update(rc(u),mid+1,r,x);
up(u);
}
void merge(int &a,int b,int l,int r)
{
if(!a||!b)
{
a=a+b;
return;
}
a=copy(a);
int mid=(l+r)>>1;
merge(lc(a),lc(b),l,mid);
merge(rc(a),rc(b),mid+1,r);
up(a);
}
int find(int u,int l,int r,int x)
{
if(!size[u]) return -1;
if(l==r) return l;
int mid=(l+r)>>1;
if(x>mid)
{
int tmp=find(rc(u),mid+1,r,x);
if(tmp!=-1) return tmp;
}
return find(lc(u),l,mid,x);
}
#undef lc
#undef rc
}
const int NN=N<<1;
int last=1,node=1,ch[NN][26],len[NN],fa[NN],rt[NN];
void insert(int c,int id)
{
int p=last,now=last=++node;
len[now]=len[p]+1,Seg::update(rt[now],1,n,id);
for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=now;
if(!p) fa[now]=1;
else
{
int q=ch[p][c];
if(len[p]+1==len[q]) fa[now]=q;
else
{
int nq=++node;
memcpy(ch[nq],ch[q],sizeof(ch[nq]));
len[nq]=len[p]+1;
fa[nq]=fa[q],fa[q]=fa[now]=nq;
for(;p&&ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
}
}
}
void init()
{
for(int i=1;i<=n;i++) insert(s[i]-'a',i);
static int id[NN];
for(int i=1;i<=node;i++) id[i]=i;
sort(id+1,id+node+1,[&](int a,int b){return len[a]<len[b];});
for(int i=node;i>=2;i--) Seg::merge(rt[fa[id[i]]],rt[id[i]],1,n);
}
void work()
{
int u=1,nlen=0;
for(int i=1;i<=m;i++)
{
int c=t[i]-'a';
while(u!=1&&!ch[u][c]) u=fa[u],nlen=len[u];
if(ch[u][c]) u=ch[u][c],nlen++;
while(1)
{
int p=Seg::find(rt[u],1,n,R);
if(p!=-1&&p-len[fa[u]]>=L)
{
nlen=min(nlen,p-L+1);
break;
}
u=fa[u],nlen=len[u];
}
lsp[i]=nlen;
}
}
}
namespace SAMT
{
const int NN=N<<1;
int last=1,node=1,ch[NN][26],len[NN],fa[NN],endpos[NN];
void clear()
{
for(int i=1;i<=node;i++)
{
memset(ch[i],0,sizeof(ch[i]));
len[i]=fa[i]=endpos[i]=0;
}
last=node=1;
}
void insert(int c,int id)
{
int p=last,now=last=++node;
len[now]=len[p]+1,endpos[now]=id;
for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=now;
if(!p) fa[now]=1;
else
{
int q=ch[p][c];
if(len[p]+1==len[q]) fa[now]=q;
else
{
int nq=++node;
memcpy(ch[nq],ch[q],sizeof(ch[nq]));
len[nq]=len[p]+1;
fa[nq]=fa[q],fa[q]=fa[now]=nq;
for(;p&&ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
}
}
}
ll work()
{
clear();
for(int i=1;i<=m;i++)
insert(t[i]-'a',i);
static int id[NN];
for(int i=1;i<=node;i++) id[i]=i;
sort(id+1,id+node+1,[&](int a,int b){return len[a]<len[b];});
for(int i=node;i>=2;i--) endpos[fa[id[i]]]=endpos[id[i]];
ll ans=0;
for(int i=2;i<=node;i++)
{
ans+=len[i]-len[fa[i]];
ans-=max(0,min(len[i],lsp[endpos[i]])-len[fa[i]]);
}
return ans;
}
}
int main()
{
// freopen("P4770_1.in","r",stdin);
// freopen("P4770_1_my.out","w",stdout);
scanf("%s%d",s+1,&q);
n=strlen(s+1);
SAMS::init();
while(q--)
{
scanf("%s",t+1);
m=strlen(t+1);
L=read(),R=read();
SAMS::work();
printf("%lld\n",SAMT::work());
}
return 0;
}
/*
abc
1
abc 3 3
*/