给一个串S,每次询问给出一个串T和区间[l,r],问T中有多少个不同的子串满足其不是S[l…r]的子串。
∣ S ∣ , ∣ T ∣ ≤ 5 ∗ 1 0 5 , q ≤ 1 0 5 , ∑ ∣ T ∣ ≤ 1 0 6 |S|,|T|\le5*10^5,q\le10^5,\sum |T|\le10^6 ∣S∣,∣T∣≤5∗105,q≤105,∑∣T∣≤106
在noi考场上打了个又臭又长的做法,还只有68分。正解其实并不算难。
先补集转化一下,变成求T有多少个子串是S[l…r]的子串。
先对S建sam,然后每次对T也建一个sam。先对T的每一个前缀,求出一个最大的L使得该前缀的L后缀是S[l…r]的子串,那么T的sam上的某一个节点的贡献就很容易求了。
由于要对S的sam建可持久化线段树来维护right集,所以时间复杂度是 O ( ( ∣ S ∣ + ∑ ∣ T ∣ ) l o g n ) O((|S|+\sum |T|)logn) O((∣S∣+∑∣T∣)logn)。
#include
#include
#include
#include
#include
typedef long long LL;
const int N=500005;
int n,m,suf[N],b[N],c[N*2],tot;
char str[N];
struct tree{int l,r;}t[N*40];
void ins(int &d,int l,int r,int x)
{
if (!d) d=++tot;
if (l==r) return;
int mid=(l+r)/2;
if (x<=mid) ins(t[d].l,l,mid,x);
else ins(t[d].r,mid+1,r,x);
}
int query(int d,int l,int r,int x)
{
if (!d) return 0;
if (l==r) return l;
int mid=(l+r)/2,ans;
if (x>mid&&(ans=query(t[d].r,mid+1,r,x))) return ans;
else return query(t[d].l,l,mid,x);
}
int merge(int x,int y)
{
if (!x||!y) return x^y;
int z=++tot;
t[z].l=merge(t[x].l,t[y].l);
t[z].r=merge(t[x].r,t[y].r);
return z;
}
struct Sam1
{
int sz,last,ch[N*2][26],fa[N*2],mx[N*2],root[N*2];
void extend(int x,int id)
{
int p,q,np,nq;
p=last;last=np=++sz;mx[sz]=mx[p]+1;
for (;p&&!ch[p][x];p=fa[p]) ch[p][x]=np;
if (!p) fa[np]=1;
else
{
q=ch[p][x];
if (mx[q]==mx[p]+1) fa[np]=q;
else
{
nq=++sz;mx[nq]=mx[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
fa[nq]=fa[q];fa[q]=fa[np]=nq;
for (;ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
}
}
ins(root[np],1,n,id);
}
void build()
{
memset(b,0,sizeof(b));
for (int i=1;i<=sz;i++) b[mx[i]]++;
for (int i=1;i<=n;i++) b[i]+=b[i-1];
for (int i=1;i<=sz;i++) c[b[mx[i]]--]=i;
for (int i=sz;i>1;i--) root[fa[c[i]]]=merge(root[c[i]],root[fa[c[i]]]);
}
}Sam1;
struct Sam2
{
int sz,last,ch[N*2][26],fa[N*2],mx[N*2],rig[N*2];
void extend(int x,int id)
{
int p,q,np,nq;
p=last;last=np=++sz;mx[np]=mx[p]+1;
memset(ch[np],0,sizeof(ch[np]));
for (;p&&!ch[p][x];p=fa[p]) ch[p][x]=np;
if (!p) fa[np]=1;
else
{
q=ch[p][x];
if (mx[q]==mx[p]+1) fa[np]=q;
else
{
nq=++sz;mx[nq]=mx[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
fa[nq]=fa[q];fa[q]=fa[np]=nq;
for (;ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
}
}
rig[np]=id;
}
void build()
{
for (int i=1;i<=m;i++) b[i]=0;
for (int i=1;i<=sz;i++) b[mx[i]]++;
for (int i=1;i<=m;i++) b[i]+=b[i-1];
for (int i=1;i<=sz;i++) c[b[mx[i]]--]=i;
for (int i=sz;i>1;i--) rig[fa[c[i]]]=rig[c[i]];
}
}Sam2;
int main()
{
scanf("%s",str+1);
n=strlen(str+1);
Sam1.last=Sam1.sz=1;
for (int i=1;i<=n;i++) Sam1.extend(str[i]-'a',i);
Sam1.build();
int q;scanf("%d",&q);
while (q--)
{
int l,r;
scanf("%s%d%d",str+1,&l,&r);
m=strlen(str+1);
int now=1,len=0;
Sam2.sz=Sam2.last=1;
memset(Sam2.ch[1],0,sizeof(Sam2.ch[1]));
for (int i=1;i<=m;i++)
{
int c=str[i]-'a';
Sam2.extend(c,i);
while (now&&!Sam1.ch[now][c]) now=Sam1.fa[now],len=Sam1.mx[now];
if (!now) now=1;
else now=Sam1.ch[now][c],len++;
while (now)
{
int x=query(Sam1.root[now],1,n,r);
if (!x||x-l+1<=Sam1.mx[Sam1.fa[now]]) now=Sam1.fa[now],len=Sam1.mx[now];
else {len=std::min(len,x-l+1);break;}
}
suf[i]=len;
}
Sam2.build();
LL ans=0;
for (int i=2;i<=Sam2.sz;i++)
{
ans+=Sam2.mx[i]-Sam2.mx[Sam2.fa[i]];
int x=Sam2.rig[i];
ans-=std::max(0,std::min(suf[x],Sam2.mx[i])-Sam2.mx[Sam2.fa[i]]);
}
printf("%lld\n",ans);
}
return 0;
}