传送门:bzoj5417
题解
首先还是那句话,要清楚后缀自动机的本质(后缀自动机详解)。
对 建一个后缀自动机,每次对 同样建一个后缀自动机。
考虑 的情况:
设 表示字符串 区间 所能匹配 的最长后缀 (若 ,则 字符没有在 中出现)。 可以同步在 的后缀自动机不断向下扩展得到,当无法匹配时,不断跳 链直到可以匹配上,保证匹配的是字符串 的后缀。
假设对于 所构成的自动机,节点 的 集合中所能表示的字符串最大长度为 , 表示该 集合字符串第一次出现的位置(这些字符串是后缀包含关系,末位置相同),节点 的 链指向节点 。
则:
实际上我们只需要在
的后缀自动机上移动,最后
枚举一遍在
的后缀自动机上统计答案(
为
后缀自动机上节点个数)。
号节点为后缀自动机起始节点。
再考虑 的情况:
稍有区别的是,在处理 时,需要判断当前的字符串是否在 的 区间中出现,可以先对 的后缀自动机每个节点按 排序,以每个节点建立线段树表示出现位置,再不断向 合并。
每次在
的后缀自动机往下扩展时,判断
中是否出现即可(
表示当前移动到的后缀自动机节点,
表示当前匹配了
的
,询问
区间的出现位置才能保证字符串出现在
内)。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+100,M=2e7+100,inf=0x3f3f3f3f;
int que,n,m,rt[N],lim[N];
char S[N],T[N];ll ans;
namespace tree{
#define mid (((l)+(r))>>1)
#define lc ch[k][0]
#define rc ch[k][1]
int tot,ch[M][2];
inline void ins(int &k,int l,int r,int pos)
{
if(!k) k=++tot;
if(l==r) return;
if(pos<=mid) ins(lc,l,mid,pos);
else ins(rc,mid+1,r,pos);
}
inline int merge(int x,int y)
{
if(!x || !y) return x+y;
int k=++tot;
lc=merge(ch[x][0],ch[y][0]);
rc=merge(ch[x][1],ch[y][1]);
return k;
}
inline bool query(int k,int l,int r,int L,int R)
{
if(!k || L>R) return false;
if(L<=l && r<=R) return true;
if(L<=mid) if(query(lc,l,mid,L,R)) return true;
if(R>mid) if(query(rc,mid+1,r,L,R)) return true;
return false;
}
}
namespace SAM{
int p,q,cnt=1,cur=1;
int ch[N][26],mx[N],fl[N],cc[N],sa[N],in[N];
inline void insert(int alp,int id)
{
p=cur;cur=++cnt;mx[cur]=id;in[cur]=1;
for(;!ch[p][alp] && p;p=fl[p]) ch[p][alp]=cur;
if(!p) fl[cur]=1;else{
q=ch[p][alp];
if(mx[q]==mx[p]+1) fl[cur]=q;else{
mx[++cnt]=mx[p]+1;
memcpy(ch[cnt],ch[q],sizeof(ch[q]));
fl[cnt]=fl[q];fl[cur]=fl[q]=cnt;
for(;ch[p][alp]==q;p=fl[p]) ch[p][alp]=cnt;
}
}
}
inline void build()
{
register int i,j;
for(i=1;i<=n;++i) insert(S[i]-'a',i);
for(i=1;i<=cnt;++i) cc[mx[i]]++;
for(i=1;i<=n;++i) cc[i]+=cc[i-1];
for(i=cnt;i>1;--i) sa[cc[mx[i]]--]=i;
for(i=cnt;i>1;--i){
j=sa[i];
if(in[j]) tree::ins(rt[j],1,n,mx[j]);
rt[fl[j]]=tree::merge(rt[fl[j]],rt[j]);
}
}
}
namespace solve{
int p,q,cur=1,cnt=1;
int ch[N][26],mx[N],fl[N],tag[N];
inline void clr()
{
register int i,j;
for(i=0;i<=cnt;++i) memset(ch[i],0,sizeof(ch[i]));
cur=cnt=1;
}
inline void ins(int alp,int id)
{
p=cur;cur=++cnt;mx[cur]=id;tag[cur]=id;
for(;!ch[p][alp] && p;p=fl[p]) ch[p][alp]=cur;
if(!p) fl[cur]=1;else{
q=ch[p][alp];
if(mx[q]==mx[p]+1) fl[cur]=q;else{
mx[++cnt]=mx[p]+1;tag[cnt]=tag[q];
memcpy(ch[cnt],ch[q],sizeof(ch[q]));
fl[cnt]=fl[q];fl[q]=fl[cur]=cnt;
for(;ch[p][alp]==q;p=fl[p]) ch[p][alp]=cnt;
}
}
}
inline ll work()
{
register int i,j,L,R,len,u,alp;
scanf("%s%d%d",T+1,&L,&R);
clr();
m=strlen(T+1);
for(len=0,u=1,i=1;i<=m;++i){
alp=T[i]-'a';
ins(alp,i);
for(;;){
if(SAM::ch[u][alp] && tree::query(rt[SAM::ch[u][alp]],1,n,L+len,R)){
len++;
u=SAM::ch[u][alp];
break;
}
if(len==0) break;
len--;
if(len==SAM::mx[SAM::fl[u]]) u=SAM::fl[u];
}
lim[i]=len;
}
for(ans=0,i=2;i<=cnt;++i)
ans+=max(0,mx[i]-max(mx[fl[i]],lim[tag[i]]));
return ans;
}
}
inline void out(ll x){if(x>9) out(x/10);putchar('0'+x%10);}
int main(){
scanf("%s",S+1);
n=strlen(S+1);
SAM::build();
scanf("%d",&que);
for(;que;--que) out(solve::work()),puts("");
return 0;
}