传送门:bzoj5417
首先还是那句话,要清楚后缀自动机的本质(后缀自动机详解)。
对 S S S建一个后缀自动机,每次对 T T T同样建一个后缀自动机。
考虑 l = 1 , r = ∣ S ∣ l=1,r=|S| l=1,r=∣S∣的情况:
设 l i m i lim_i limi表示字符串 T T T区间 [ 1 , i ] [1,i] [1,i]所能匹配 S S S的最长后缀 [ i − l i m i + 1 , i ] ( l i m i > 0 ) [i-lim_i+1,i](lim_i>0) [i−limi+1,i](limi>0)(若 l i m i = 0 lim_i=0 limi=0,则 T i T_i Ti字符没有在 S S S中出现)。 l i m i lim_i limi可以同步在 S S S的后缀自动机不断向下扩展得到,当无法匹配时,不断跳 f a i l fail fail链直到可以匹配上,保证匹配的是字符串 T [ 1 , i ] T[1,i] T[1,i]的后缀。
假设对于 S S S所构成的自动机,节点 i i i的 r i g h t right right集合中所能表示的字符串最大长度为 m x i mx_i mxi, t a g i tag_i tagi表示该 r i g h t right right集合字符串第一次出现的位置(这些字符串是后缀包含关系,末位置相同),节点 i i i的 f a i l fail fail链指向节点 f l i fl_i fli。
则: a n s = ∑ i = 2 c n t m a x ( 0 , m x i − m a x ( m x f l i , l i m t a g i ) ) ans=\sum \limits_{i=2}^{cnt} max(0,mx_i-max(mx_{fl_i},lim_{tag_i})) ans=i=2∑cntmax(0,mxi−max(mxfli,limtagi))
实际上我们只需要在 S S S的后缀自动机上移动,最后 O ( c n t ) O(cnt) O(cnt)枚举一遍在 T T T的后缀自动机上统计答案( c n t cnt cnt为 T T T后缀自动机上节点个数)。
1 1 1号节点为后缀自动机起始节点。
再考虑 l ≠ 1 , r ≠ ∣ S ∣ l\neq 1,r\neq |S| l̸=1,r̸=∣S∣的情况:
稍有区别的是,在处理 l i m i lim_i limi时,需要判断当前的字符串是否在 S S S的 [ l , r ] [l,r] [l,r]区间中出现,可以先对 S S S的后缀自动机每个节点按 m x i mx_i mxi排序,以每个节点建立线段树表示出现位置,再不断向 f l i fl_i fli合并。
每次在 S S S的后缀自动机往下扩展时,判断 [ r t [ p o s ] , l + l e n , r ] [rt[pos],l+len,r] [rt[pos],l+len,r]中是否出现即可( p o s pos pos表示当前移动到的后缀自动机节点, l e n len len表示当前匹配了 T T T的 [ i − l e n + 1 , i ] [i-len+1,i] [i−len+1,i],询问 [ l + l e n , r ] [l+len,r] [l+len,r]区间的出现位置才能保证字符串出现在 [ l , r ] [l,r] [l,r]内)。
O ( ∣ S ∣ + l o g ∣ S ∣ + ∑ ( ∣ T ∣ + ∣ T ∣ l o g ∣ S ∣ ) ) O(|S|+log|S|+\sum(|T|+|T|log|S|)) O(∣S∣+log∣S∣+∑(∣T∣+∣T∣log∣S∣))
#include
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;
}