给出一个字符串(S),然后有若干次询问,每次统计字符串(T)中出现过的(S_{l..r})中没有出现过的子串的个数。
(|S|,|T|le 5*10^5,sum |T|le 10^6)
自己想出来的做法:(O(nlg ^2 n)),直接搞出SAM之后线段树+树上二分跳祖先。具体就是:对(S)建出SAM之后,离线枚举询问右端点。主要问题是对(T)的每个前缀求出作为(S_{l..r})子串的最长后缀,即祖先中深度最大(x)满足(mxr_x-len_x+1ge l)。显然可二分。
正解:(O(nlg n))。详细一点说明(有些部分上面的做法也需要用到):
同样是对(S)建出SAM,也是要对(T)的每个前缀求出作为(S_{l..r})子串的最长后缀。求出来之后去重即可(对(T)建SAM,建出(T)的fail树,打标记表示其祖先不计入答案)。
对于这个问题,如果询问(S_{1..|S|})就可以直接在SAM上跑。现在限制了范围,魔改一下跑的方式:(假设当前点为(p),匹配的长度为(len),新增的字符为(c))
原来:如果存在(p.trans(c))则(p)跳过去且(lenleftarrow len+1),否则跳(fail),且(lenleftarrow p.fail.len)。(细致一些:否则(lenleftarrow len-1),如果(len=p.fail)则(p)跳(fail)。时间复杂度不变。便于推广)
魔改:处理出每个点的(right)集合。定义(p.query(l,r))表示是否(exist xin p.right,xin [l,r])。如果(p.trans(c).query(l+len,r))为真,则跳过去且(lenleftarrow len+1);否则(lenleftarrow len-1),如果(len=p.fail)则跳(fail))
可以如此说明它的正确性:当前已经配了合法的(len)长度,现在配(len+1),显然(p.trans(c).query(l+len,r))为必要条件,并且根据(right)集合的定义得知这也是充分的。所以这是充分必要条件。
处理(right)集合可以用可持久化线段树合并。
using namespace std;
#include <bits/stdc++.h>
#define N 500005
#define ll long long
int n,m;
char s[N],t[N];
struct SAM{
struct Node{
Node *c[26],*fa;
int len;
} d[N*2],*S,*T;
int id(Node *x){return x-d;}
int cnt;
Node *newnode(){
++cnt;
memset(&d[cnt],0,sizeof d[cnt]);
return &d[cnt];
}
void init(){
T=S=&d[cnt=1];
memset(&d[1],0,sizeof d[1]);
}
void insert(int ch){
Node *nw=newnode(),*p,*q;
nw->len=T->len+1;
for (p=T;p && !p->c[ch];p=p->fa)
p->c[ch]=nw;
if (!p)
nw->fa=S;
else{
q=p->c[ch];
if (p->len+1==q->len)
nw->fa=q;
else{
Node *clone=newnode();
memcpy(clone,q,sizeof *q);
clone->len=p->len+1;
for (;p && p->c[ch]==q;p=p->fa)
p->c[ch]=clone;
nw->fa=q->fa=clone;
}
}
T=nw;
}
void build(char *s){
init();
for (;*s;++s)
insert(*s-'a');
}
} S,T;
struct Seg{
Seg *l,*r;
} d[N*50],*null,*rt[N*2];
int cnt;
Seg *newnode(){return &(d[++cnt]={null,null});}
void insert(int x,Seg *&t,int l=1,int r=n){
if (t==null) t=newnode();
if (l==r) return;
int mid=l+r>>1;
if (x<=mid) insert(x,t->l,l,mid);
else insert(x,t->r,mid+1,r);
}
Seg *merge(Seg *a,Seg *b){
if (a==null) return b;
if (b==null) return a;
Seg *c=newnode();
c->l=merge(a->l,b->l);
c->r=merge(a->r,b->r);
return c;
}
bool query(int st,int en,Seg *t,int l=1,int r=n){
if (t==null) return 0;
if (st<=l && r<=en) return 1;
int mid=l+r>>1;
bool res=0;
if (st<=mid) res=query(st,en,t->l,l,mid);
if (mid<en && !res) res=query(st,en,t->r,mid+1,r);
return res;
}
struct EDGE{
int to;
EDGE *las;
} e[N*2];
int ne;
EDGE *last[N*2];
void link(int u,int v){
e[ne]={v,last[u]};
last[u]=e+ne++;
}
void dfsS(int x){
for (EDGE *ei=last[x];ei;ei=ei->las){
dfsS(ei->to);
rt[x]=merge(rt[x],rt[ei->to]);
}
}
void buildG(){
S.build(s+1);
null=d;
*null={null,null};
for (int i=1;i<=S.cnt;++i)
rt[i]=null;
SAM::Node *t=S.S;
for (int i=1;i<=n;++i){
t=t->c[s[i]-'a'];
insert(i,rt[S.id(t)]);
}
for (int i=2;i<=S.cnt;++i)
link(S.id(S.d[i].fa),i);
dfsS(1);
}
int bz[N*2],dep[N*2];
void buildT(){
T.build(t+1);
memset(last,0,sizeof(EDGE*)*(T.cnt+1));
ne=0;
for (int i=2;i<=T.cnt;++i){
link(T.id(T.d[i].fa),i);
dep[i]=T.d[i].len;
bz[i]=0;
}
bz[1]=0;
}
ll ans;
void dfsT(int x){
for (EDGE *ei=last[x];ei;ei=ei->las){
dfsT(ei->to);
bz[x]=max(bz[x],bz[ei->to]);
ans+=max(dep[ei->to]-max(bz[ei->to],dep[x]),0);
}
}
int main(){
freopen("name.in","r",stdin);
freopen("name.out","w",stdout);
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
scanf("%s",s+1);
n=strlen(s+1);
buildG();
int Q;
scanf("%d",&Q);
while (Q--){
int l,r;
scanf("%s%d%d",t+1,&l,&r);
m=strlen(t+1);
buildT();
SAM::Node *q=S.S,*p=T.S;
int len=0;
for (int i=1;i<=m;++i){
while (len && (!q->c[t[i]-'a'] || l+len>r || !query(l+len,r,rt[S.id(q->c[t[i]-'a'])]))){
--len;
if (len==q->fa->len)
q=q->fa;
}
if (q->c[t[i]-'a'] && query(l+len,r,rt[S.id(q->c[t[i]-'a'])]))
q=q->c[t[i]-'a'],len++;
p=p->c[t[i]-'a'];
bz[T.id(p)]=max(bz[T.id(p)],len);
}
ans=0;
dfsT(1);
printf("%lld
",ans);
}
return 0;
}