题目:luogu4770.
题目大意:给定一个串 S S S,和 m m m组询问,每组询问包含一个串 T i T_i Ti和一个区间 [ l i , r i ] [l_i,r_i] [li,ri],表示询问 T i T_i Ti有多少个本质不同的子串不是 S [ l i , r i ] S[l_i,r_i] S[li,ri]的子串.
1 ≤ ∣ S ∣ , ∣ T i ∣ ≤ 5 ∗ 1 0 5 , 1 ≤ ∑ ∣ T i ∣ ≤ 1 0 6 1\leq|S|,|T_i|\leq 5*10^5,1\leq \sum |T_i|\leq 10^6 1≤∣S∣,∣Ti∣≤5∗105,1≤∑∣Ti∣≤106.
把原问题转化为求 T i T_i Ti本质不同字串个数减去既是 S [ l i , r i ] S[l_i,r_i] S[li,ri]子串又是 T i T_i Ti子串的串个数,然后考虑 l i = 1 , r i = ∣ S ∣ l_i=1,r_i=|S| li=1,ri=∣S∣的部分分.
先对 S S S建立SAM,发现并不是很好操作,我们并不能每次大力扫一遍 S S S来处理,所以考虑对每个询问的 T i T_i Ti建立一个SAM.现在考虑求出 T i T_i Ti上每个状态的一个 a n s ans ans值,表示这个状态可以表示的串中最长的是 S S S子串的串长.
同时把 T i T_i Ti放入 S S S和 T i T_i Ti的SAM上运行,当 S S S运行到某个状态时,找到这个状态的一个在parent树上深度最深且有连向下一个字符的转移边的状态转移,然后对应的在 T i T_i Ti上转移.
这个 68 68 68分的做法很简单,但是要注意不能让跑到空状态里(即下标为 0 0 0的状态).
然后考虑满分做法.很容易发现满分做法与 68 68 68分做法相差在在 S S S的SAM上运行到某个状态时,发现这个状态并不能表示一个在 [ l i , r i ] [l_i,r_i] [li,ri]内的串,所以考虑直接维护Right集合来判定是否能够往下跳,不能往下跳就缩小长度继续尝试.
维护Right集合可以通过线段树合并来实现,不过由于要维护任意时刻任意节点的Right集合,实现时需要把本来直接覆盖在一棵树上的节点直接新开节点存.
时空复杂度 O ( n ( Σ + log n ) ) O(n(\Sigma+\log n)) O(n(Σ+logn)).
代码如下:
#include
using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=1000000,C=26;
int n;
char c[N+9];
struct segment_tree{
struct tree{
int s[2],sum;
}tr[N*2*C+9];
int cn;
int new_node(int x){tr[++cn]=tree();tr[cn].sum=x;return cn;}
int insert(int x,int l,int r,int k){
if (!k) k=new_node(0);
++tr[k].sum;
if (l==r) return k;
int mid=l+r>>1;
if (x<=mid) tr[k].s[0]=insert(x,l,mid,tr[k].s[0]);
else tr[k].s[1]=insert(x,mid+1,r,tr[k].s[1]);
return k;
}
int query(int L,int R,int l,int r,int k){
if (!k||L>R) return 0;
if (l==L&&r==R) return tr[k].sum;
int mid=l+r>>1;
if (R<=mid) return query(L,R,l,mid,tr[k].s[0]);
else if (L>mid) return query(L,R,mid+1,r,tr[k].s[1]);
else return query(L,mid,l,mid,tr[k].s[0])+query(mid+1,R,mid+1,r,tr[k].s[1]);
}
int merge(int u,int v){
if (u==0||v==0) return u+v;
int k=new_node(tr[u].sum+tr[v].sum);
tr[k].s[0]=merge(tr[u].s[0],tr[v].s[0]);
tr[k].s[1]=merge(tr[u].s[1],tr[v].s[1]);
return k;
}
}tr;
struct suffix_automaton{
struct automaton{
int s[C],len,par;
}tr[N*2+9];
int cn,last,cnt[N*2+9];
void build(){tr[cn=last=1]=automaton();}
int par(int x){return tr[x].par;}
int len(int x){return tr[x].len;}
int son(int k,int x){return tr[k].s[x];}
int tru(int x){return cnt[x];}
void extend(int x){
int p=last,np=++cn;
tr[np]=automaton();tr[np].len=tr[p].len+1;cnt[np]=1;
last=np;
while (p&&!tr[p].s[x]) tr[p].s[x]=np,p=tr[p].par;
if (!p) tr[np].par=1;
else{
int q=tr[p].s[x];
if (tr[p].len+1==tr[q].len) tr[np].par=q;
else{
tr[++cn]=tr[q];tr[cn].len=tr[p].len+1;cnt[cn]=0;
tr[q].par=tr[np].par=cn;
while (p&&tr[p].s[x]==q) tr[p].s[x]=cn,p=tr[p].par;
}
}
}
}s,t;
int v[N+9],q[N*2+9],rot[N*2+9];
void get_right(){
for (int i=1;i<=s.cn;++i) ++v[s.len(i)];
for (int i=1;i<=n;++i) v[i]+=v[i-1];
for (int i=s.cn;i>=1;--i) q[v[s.len(i)]--]=i;
for (int i=1;i<=s.cn;++i){
rot[i]=tr.new_node(0);
if (s.tru(i)) rot[i]=tr.insert(s.len(i),1,n,rot[i]);
}
for (int i=s.cn;i>=2;--i)
rot[s.par(q[i])]=tr.merge(rot[s.par(q[i])],rot[q[i]]);
}
int ans[N*2+9];
LL solve(){
int m,tp=1,sp=1,len=0,l,r;
LL sum=0;
scanf("%s%d%d",c+1,&l,&r);
m=strlen(c+1);
t.build();
for (int i=1;i<=m;++i)
t.extend(c[i]-'a');
for (int i=1;i<=t.cn;++i) ans[i]=0;
for (int i=1;i<=m;++i){
while (sp&&!(s.son(sp,c[i]-'a')&&tr.query(l+len,r,1,n,rot[s.son(sp,c[i]-'a')]))){
--len;
if (s.len(s.par(sp))>=len) sp=s.par(sp);
}
if (!sp) sp=1,len=0;
else sp=s.son(sp,c[i]-'a'),++len;
tp=t.son(tp,c[i]-'a');
while (t.par(tp)&&t.len(t.par(tp))>=len) tp=t.par(tp);
ans[tp]=max(ans[tp],len);
}
for (int i=1;i<=m;++i) v[i]=0;
for (int i=1;i<=t.cn;++i) ++v[t.len(i)];
for (int i=1;i<=m;++i) v[i]+=v[i-1];
for (int i=t.cn;i>=1;--i) q[v[t.len(i)]--]=i;
for (int i=t.cn;i>=2;--i)
if (ans[q[i]]) ans[t.par(q[i])]=t.len(t.par(q[i]));
for (int i=2;i<=t.cn;++i){
sum+=(LL)t.len(i)-t.len(t.par(i));
if (ans[i]) sum-=(LL)ans[i]-t.len(t.par(i));
}
return sum;
}
Abigail into(){
scanf("%s",c+1);
n=strlen(c+1);
}
Abigail work(){
s.build();
for (int i=1;i<=n;++i)
s.extend(c[i]-'a');
get_right();
}
Abigail getans(){
int q;
scanf("%d",&q);
while (q--)
printf("%lld\n",solve());
}
int main(){
into();
work();
getans();
return 0;
}