Case1
先考虑一个弱化版本,也就是每次询问区间是 l = 1 & & r = ∣ S ∣ l=1\&\&r=|S| l=1&&r=∣S∣
怎么求串 T T T拥有和 S S S不同的本质不同串数量??
考虑对 T T T的每一个前缀 T [ 1... i ] T[1...i] T[1...i]求出一个 r e s [ i ] res[i] res[i]表示和串 S S S的最长公共后缀
这个对 S S S建一个 S A M SAM SAM在上面跑就好了
然后 ∑ r e s [ i ] \sum\limits res[i] ∑res[i]包含了很多重复的子串,去掉这些重复的子串,剩下的就是本质不同且和 S S S相同的子串
对 T T T也建一个 S A M SAM SAM,考虑自动机上的节点 k k k
节点 k k k上有 l [ k ] − l [ f a k ] l[k]-l[fa_k] l[k]−l[fak]种子串,其中有多少在串 S S S中出现过呢??
我们从节点 k k k取出 e n d p o s endpos endpos集合中的任何一个结束位置 m i k mi_k mik(代码中直接取第一个,因为好写)
说明 T [ m i k − r e s [ m i k ] + 1 , m i k ] T[mi_k-res[mi_k]+1,mi_k] T[mik−res[mik]+1,mik]的所有后缀在 S S S中出现过
而 k k k节点包含 T [ m i k − l [ f a k ] , m i k ] , T [ m i k − l [ f a k ] − 1 , m i k ] . . . T [ m i k − l [ k ] + 1 , m i i ] T[mi_k-l[fa_k],mi_k],T[mi_k-l[fa_k]-1,mi_k]...T[mi_k-l[k]+1,mi_i] T[mik−l[fak],mik],T[mik−l[fak]−1,mik]...T[mik−l[k]+1,mii]这些串
减去这部分重复的就好了
所以答案是
∑ k ∈ S A M t m a x ( 0 , l [ k ] − m a x ( l [ f a k ] , r e s [ m i k ] ) ) \sum\limits_{k\in SAM_t}max(0,l[k]-max(l[fa_k],res[mi_k])) k∈SAMt∑max(0,l[k]−max(l[fak],res[mik]))
Case 2
考虑每次询问 S S S的区间任意怎么写
实际上就是在用 S S S的自动机匹配 T T T时不能有边就转移了
有边,还需要考虑一下当前匹配的长度为 l e n len len的串是否在区间 [ L , R ] [L,R] [L,R]中
实际上就是判断下一个节点是否存在 [ L + l e n − 1 , R ] [L+len-1,R] [L+len−1,R]的 e n d p o s endpos endpos
线段树合并维护 e n d p o s endpos endpos即可
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6+10+2e5+10;
const int N = 1e6*22;
//下面是线段树
int sum[N],rt[maxn],ls[N],rs[N],Line,n;
void push_up(int r)
{
sum[r] = sum[ls[r]] + sum[rs[r]];
}
void add(int &root,int l,int r,int index)
{
if( l>index || r<index ) return;
if( !root ) root = ++Line;
if( l==r&&l==index ){ sum[root]=1; return; }
int mid = l+r>>1;
add( ls[root],l,mid,index); add( rs[root],mid+1,r,index );
push_up( root );
}
int ask(int &root,int l,int r,int L,int R)
{
if( l>R || r<L ) return 0;
if( root==0 ) return 0;
if( l>=L&&r<=R ) return sum[root];
int mid = l+r>>1;
return ask( ls[root],l,mid,L,R)+ask( rs[root],mid+1,r,L,R );
}
int merge(int x,int y,int l,int r)
{
if( !x || !y ) return x|y;
int p = ++Line;
if( l==r ){ sum[p]=sum[x] | sum[y]; return p; }
int mid = l+r>>1;
ls[p] = merge( ls[x],ls[y],l,mid );
rs[p] = merge( rs[x],rs[y],mid+1,r );
push_up( p );
return p;
}
char a[maxn];
int res[maxn];
struct SAM
{
int zi[maxn][27],fa[maxn],l[maxn],mi[maxn],las = 1, id = 1;
void insert(int c,int ok)
{
int p = las, np = ++id; las = id;
mi[np] = l[np] = l[p]+1;
if( ok ) add( rt[np],1,n,l[np] );
for( ;p&&zi[p][c]==0;p=fa[p] ) zi[p][c] = np;
if( !p ) fa[np] = 1;
else
{
int q = zi[p][c];
if( l[q] == l[p]+1 ) fa[np] = q;
else
{
int nq=++id;
memcpy( zi[nq],zi[q],sizeof zi[nq] );
l[nq] = l[p]+1, fa[nq] = fa[q]; mi[nq] = mi[q];
fa[np] = fa[q] = nq;
for( ;p&&zi[p][c]==q;p=fa[p] ) zi[p][c] = nq;
}
}
}
long long solve()
{
long long ans = 0;
for(int i=1;i<=id;i++) ans += max( 0,l[i]-max( l[fa[i]],res[mi[i]] ) );
return ans;
}
void init()
{
for(int i=1;i<=id;i++)
{
memset( zi[i],0,sizeof zi[i] );
fa[i] = l[i] = mi[i] = 0;
}
las = id = 1;
}
}s1,s2;
vector<int>vec[maxn];
void dfs(int u,int father)//树上倍增
{
for(auto v:vec[u] )
{
dfs( v,u );
rt[u] = merge( rt[u],rt[v],1,n );
}
}
void build()
{
scanf("%s",a+1 ); n = strlen( a+1 );
for(int i=1;i<=n;i++) s1.insert( a[i]-'a',1 );
for(int i=2;i<=s1.id;i++) vec[s1.fa[i]].push_back( i );
dfs(1,0);//线段树合并
}
int main()
{
build();
int q; scanf("%d",&q);
while( q-- )
{
int L,R,p=1;
scanf("%s%d%d",a+1,&L,&R ); int le = strlen( a+1 );
for(int i=1;i<=le;i++)
{
res[i] = res[i-1];
while( 1 )//一直去匹配即可
{
int v = s1.zi[p][a[i]-'a'];
if( v&&ask( rt[v],1,n,L+res[i],R) ) { p = v; res[i]++; break; }
if( !res[i] ) { p=1 ; break; }
res[i]--;
if( res[i]==s1.l[s1.fa[p]] ) p = s1.fa[p];
}
s2.insert( a[i]-'a',0 );
}
printf("%lld\n",s2.solve() );
s2.init();
}
}