题目链接:
题目大意:给出一个字符串$S$及$q$次询问,每次询问一个字符串$T$有多少本质不同的子串不是$S[l,r]$的子串($S[l,r]$表示$S$串的第$l$个字符到第$r$个字符组成的子串)。
首先考虑$l=1,r=|S|$的情况,对$T$串建立后缀自动机,可以知道$T$串本质不同的子串个数就是后缀自动机上每个点的$len[i]-len[pre[i]]$($len[i]$代表这个点所能表示的最长串长度),这也就是后缀自动机上每个点贡献的子串个数。对于每个点它贡献的串显然是它的后缀,但这些后缀中会有一些是$S$串的子串,所以就需要减掉这部分的贡献。我们再对$S$建立后缀自动机并将$T$串在$S$串上匹配求出$mx[i]$表示$T[1,i]$的最长后缀是$S$串的子串的长度。匹配时我们用$cnt$记录匹配完$T$串每个字符$i$之后得到的$mx[i]$,显然如果能往下走就$cnt++$,否则就跳父亲节点直到能往下走为止(假设跳父亲节点跳到$p$),然后将$cnt$置成$len[p]$再继续往下走,这样就能得到$T$串中每个点的$mx[i]$。对于$T$串的后缀自动机上每个点$i$再记录它$endpos$集合中最小的位置$pos[i]$(因为$endpos$集合中任意位置的后缀都相同,所以随便哪个都行,但第一个显然在建后缀自动机时就能记录)。那么最后的答案就是$ans=sum max(0,len[i]-max(mx[pos[i]],len[pre[i]]))$,其中$i$为后缀自动机上的所有节点。
现在考虑$l,r$任意的情况,显然$mx[i]$要变成$T[1,i]$的最长后缀是$S[l,r]$的子串的最长长度。所以我们需要维护$S$串后缀自动机上每个节点的$endpos$集合,对于$S$串后缀自动机上每个节点动态开点建一棵线段树存这个节点的$endpos$集合,然后在$pre$树上从下往上线段树合并即可维护出每个点的$endpos$集合。那么当$T$串在$S$串后缀自动机上匹配时,我们需要知道接下来要往下走的节点的$endpos$集合中是否有在$[l+cnt,r]$之中的。如果有就往下走并$cnt++$,否则一点点减小$cnt$并继续在线段树上查找,当$cnt$减小到$len[pre[i]]$时说明当前点不能匹配$T$串的下一个字符,这时就要跳到$pre[i]$再重复上述操作,最后求答案的部分与$l=1,r=|S|$的情况相同。注意在线段树合并时因为要保留被合并的树,所以在合并时要新开节点。
#include<set> #include<map> #include<queue> #include<stack> #include<cmath> #include<cstdio> #include<bitset> #include<vector> #include<cstring> #include<iostream> #include<algorithm> #define ll long long using namespace std; ll ans; int n,q; int L,R; int tot; char s[500010]; char t[500010]; int to[1000010]; int mx[500010]; int head[1000010]; int next[1000010]; int root[1000010]; void add(int x,int y) { next[++tot]=head[x]; head[x]=tot; to[tot]=y; } namespace segment_tree { int cnt; int ls[36000010]; int rs[36000010]; void insert(int &rt,int l,int r,int k) { if(!rt) { rt=++cnt; } if(l==r) { return ; } int mid=(l+r)>>1; if(k<=mid) { insert(ls[rt],l,mid,k); } else { insert(rs[rt],mid+1,r,k); } } int merge(int x,int y) { if(!x||!y) { return x+y; } int rt=++cnt; ls[rt]=merge(ls[x],ls[y]); rs[rt]=merge(rs[x],rs[y]); return rt; } int query(int rt,int l,int r,int L,int R) { if(L>R) { return 0; } if(!rt) { return 0; } if(L<=l&&r<=R) { return 1; } int mid=(l+r)>>1; int res=0; if(L<=mid) { res|=query(ls[rt],l,mid,L,R); } if(R>mid) { res|=query(rs[rt],mid+1,r,L,R); } return res; } } void dfs(int x) { for(int i=head[x];i;i=next[i]) { dfs(to[i]); root[x]=segment_tree::merge(root[x],root[to[i]]); } } namespace SAM_S { int trs[1000010][26]; int len[1000010]; int pre[1000010]; int cnt=1; int last=1; void insert(int x,int y) { int p=last; int np=++cnt; last=np; len[np]=len[p]+1; segment_tree::insert(root[np],1,n,y); for(;p&&!trs[p][x];p=pre[p]) { trs[p][x]=np; } if(!p) { pre[np]=1; } else { int q=trs[p][x]; if(len[q]==len[p]+1) { pre[np]=q; } else { int nq=++cnt; pre[nq]=pre[q]; memcpy(trs[nq],trs[q],sizeof(trs[q])); pre[np]=pre[q]=nq; len[nq]=len[p]+1; for(;p&&trs[p][x]==q;p=pre[p]) { trs[p][x]=nq; } } } } void build() { for(int i=1;i<=n;i++) { insert(s[i]-'a',i); } for(int i=2;i<=cnt;i++) { add(pre[i],i); } dfs(1); } } namespace SAM_T { int trs[1000010][26]; int len[1000010]; int pre[1000010]; int pos[1000010]; int cnt; int last; void initial() { memset(trs,0,(cnt+2)*sizeof(trs[0])); cnt=last=1; } void insert(int x,int y) { int p=last; int np=++cnt; last=np; len[np]=len[p]+1; pos[np]=y; for(;p&&!trs[p][x];p=pre[p]) { trs[p][x]=np; } if(!p) { pre[np]=1; } else { int q=trs[p][x]; if(len[q]==len[p]+1) { pre[np]=q; } else { int nq=++cnt; pre[nq]=pre[q]; pos[nq]=pos[q]; memcpy(trs[nq],trs[q],sizeof(trs[q])); pre[np]=pre[q]=nq; len[nq]=len[p]+1; for(;p&&trs[p][x]==q;p=pre[p]) { trs[p][x]=nq; } } } } void build() { initial(); int tlen=strlen(t+1); for(int i=1;i<=tlen;i++) { insert(t[i]-'a',i); } } } void solve(int L,int R) { ans=0; int tlen=strlen(t+1); int now=1; int res=0; for(int i=1;i<=tlen;i++) { while(1) { if(!segment_tree::query(root[SAM_S::trs[now][t[i]-'a']],1,n,L+res,R)) { if(!res) { break; } res--; if(res==SAM_S::len[SAM_S::pre[now]]) { now=SAM_S::pre[now]; } } else { res++; now=SAM_S::trs[now][t[i]-'a']; break; } } mx[i]=res; } for(int i=2;i<=SAM_T::cnt;i++) { ans+=max(0,SAM_T::len[i]-max(SAM_T::len[SAM_T::pre[i]],mx[SAM_T::pos[i]])); } } int main() { scanf("%s",s+1); n=strlen(s+1); SAM_S::build(); scanf("%d",&q); while(q--) { scanf("%s",t+1); scanf("%d%d",&L,&R); SAM_T::build(); solve(L,R); printf("%lld ",ans); } }