J. Dictionary
time limit per test 4 seconds
memory limit per test 1024 megabytes
You're going to learn words from a dictionary for the next q days. The dictionary is a string S = s 1 s 2 ⋯ s n S=s_1s_2⋯s_n S=s1s2⋯sn of length n consisting of lower-cased English letters, and a word is a substring of S.
On the i-th day, you will learn all the words which start with S [ l i : r i ] = s l i s l i + 1 ⋯ s r i S[l_i:r_i]=s_{l_i}s_{l_{i+1}}⋯s_{r_i} S[li:ri]=slisli+1⋯sri (that is to say, S [ l i : r i ] S[l_i:r_i] S[li:ri] is a prefix of these words). After each day, count how many different words you have already learned starting from the first day.
Input
There are multiple test cases. The first line of the input contains an integer T ( 1 ≤ T ≤ 10 4 ) T (1\leq T\leq10^4) T(1≤T≤104) indicating the number of test cases. For each test case:
The first line contains a string s 1 s 2 ⋯ s n s_1s_2⋯s_n s1s2⋯sn of length n ( 1 ≤ n ≤ 2 × 10 5 ) n (1\leq n\leq2\times10^5) n(1≤n≤2×105) consisting of lower-cased English letters, indicating the dictionary.
The second line contains an integer q ( 1 ≤ q ≤ 2 × 10 5 ) q\ (1\leq q\leq2\times10^5) q (1≤q≤2×105), indicating the number of days you'll learn words.
For the following q lines, the i-th line contains two integers l i l_i li and r i r_i ri ( 1 ≤ l i ≤ r i ≤ n ) (1\leq l_i\leq r_i\leq n) (1≤li≤ri≤n), indicating that on the i-th day, you will learn all the words with a prefix of s l i s l i + 1 ⋯ s r i s_{l_i}s_{l_{i+1}}⋯s_{r_i} slisli+1⋯sri.
It's guaranteed that neither the sum of n nor the sum of q of all test cases will exceed 2 × 10 5 2\times10^5 2×105.
Output
For each test case, output one line containing q integers c 1 , c 2 , ⋯ c q c_1,c_2,⋯c_q c1,c2,⋯cq separated by a space, where ci is the number of different words you have learned from the 1-st day to the i-th day (both inclusive).
Example
Input
2
abcabd
3
1 3
1 2
6 6
aaa
3
3 3
2 3
1 3
Outpu
4 6 7
3 3 3
Note
For the first sample test case:
On the 1-st day you learned a b c , a b c a , a b c a b , abc, abca, abcab, abc,abca,abcab, and a b c a b d abcabd abcabd. So the answer is 4.
On the 2-nd day you learned a b , a b c , a b c a , a b c a b , a b c a b d , ab, abc, abca, abcab, abcabd, ab,abc,abca,abcab,abcabd, and a b d abd abd. There are 4 words you have already learned, so the answer is 4 + ( 6 − 4 ) = 6 4+(6−4)=6 4+(6−4)=6.
On the 3-rd day you learned d d d, which you haven't learned before. So the answer is 6 + 1 = 7 6+1=7 6+1=7.
For the second sample test case, you have learned all the words ( a , a a , a a a ) (a, aa, aaa) (a,aa,aaa) on the 1-st day, so the answer is always 3.
思路:在后缀数组里有相同前缀 S [ l i : r i ] S[l_i:r_i] S[li:ri]的后缀是连续的,即在某个区间 [ L , R ] [L,R] [L,R]内的后缀都有相同的前缀 S [ l i : r i ] S[l_i:r_i] S[li:ri],所以我们只需统计这个区间 [ L , R ] [L,R] [L,R]对答案的贡献即可。
假设 a k a_k ak代表以 S s a [ k ] S_{sa[k]} Ssa[k]为开头的后缀字符串中还未被统计到答案的后缀字符串数量,那么此时对答案的贡献即是 h e i g h t L − ( r i − l i ) + ∑ L ≤ k ≤ R ( a k − h e i g h t k ) height_L-(r_i-l_i)+\sum_{L\leq k\leq R}{(a_k-height_k)} heightL−(ri−li)+L≤k≤R∑(ak−heightk)可以用线段树根据 h e i g h t height height 数组建树,线段树统计 h e i g h t height height 和 a a a 的区间和,区间 [ L , R ] [L,R] [L,R] 也可以利用线段树求出。
每次求完答案贡献后,即可将对应区间内的 h e i g h t height height 和 a a a 缩减至 r i − l i r_i-l_i ri−li,保证 a k ≥ h e i g h t k a_k\geq height_k ak≥heightk 始终成立。
cpp
#include<bits/stdc++.h>
#define fi first
#define se second
#define lson (k<<1)
#define rson (k<<1)+1
#define mid ((l+r)/2)
#define sz(x) ssize(x)
#define pii pair<int,int>
#define ull unsigned long long
using namespace std;
const int MAX=2e5+10;
const int MOD=998244353;
const int INF=INT_MAX/2;
const double PI=acos(-1.0);
typedef long long ll;
struct SA
{
char s[MAX];
int n;
vector<int>sa,rk,height,c;
vector<vector<int>>nex;
int init()
{
scanf("%s",s);
n=strlen(s);
rk.resize(n);height.resize(n);
sa.resize(n);nex.resize(n);
int m=*max_element(s,s+n);
c.assign(m+1,0);
for(int i=0;i<n;i++)c[rk[i]=s[i]]++;
for(int i=1;i<=m;i++)c[i]+=c[i-1];
for(int i=0;i<n;i++)sa[--c[s[i]]]=i;
for(int w=1;;w<<=1)
{
m=RadixSort(m,w);
if(m+1>=n)break;
}
// for(int i=0;i<n;i++)printf("%d ",rk[i]);puts("");
for(int i=0,k=0;i<n;i++)
{
if(rk[i]==0)continue;
if(k)k--;
while(i+k<n&&sa[rk[i]-1]+k<n&&s[i+k]==s[sa[rk[i]-1]+k])k++;
height[rk[i]]=k;
}
// for(int i=0;i<n;i++)printf("%d ",height[i]);puts("");
for(int i=n-1;i>=0;i--)
{
nex[i].assign(31,n);
nex[i][0]=height[i];
for(int j=1,w=2;i+w-1<n;j++,w<<=1)nex[i][j]=min(nex[i][j-1],nex[i+w/2][j-1]);
}
return n;
}
int RadixSort(int m,int w) {
c.assign(m+1,0);
static vector<int>tmp; tmp.resize(n);
static vector<int>oldrk; oldrk=rk;
int p=0;
for(int i=n-1;i>=max(0,n-w);i--)tmp[p++]=i;
for(int i=0;i<n;i++)if(sa[i]>=w)tmp[p++]=sa[i]-w;
for(int i=0;i<n;i++)c[rk[i]]++;
for(int i=1;i<=m;i++)c[i]+=c[i-1];
for(int i=n-1;i>=0;i--)sa[--c[rk[tmp[i]]]]=tmp[i];
auto neq=[&](int i,int j,int w){
if(oldrk[i]!=oldrk[j])return true;
int rki=i+w<n?oldrk[i+w]:-1;
int rkj=j+w<n?oldrk[j+w]:-1;
return rki!=rkj;
};
p=0;
rk[sa[0]]=0;
for(int i=1;i<n;i++)rk[sa[i]]=neq(sa[i],sa[i-1],w)?++p:p;
return p;
}
int LCP(int x,int y)
{
int ret=n-x;
x=rk[x],y=rk[y];
if(x>y)swap(x,y);
x++;
for(int j=30;j>=0;j--)
{
if(x+(1<<j)-1>y)continue;
ret=min(ret,nex[x][j]);
x+=1<<j;
}
return ret;
}
}sa;
struct lenka
{
int L[MAX<<2],R[MAX<<2],minh[MAX<<2],maxh[MAX<<2],a[MAX<<2],tag[MAX<<2];
ll sumh[MAX<<2],suma[MAX<<2];
void build(int l,int r,int k=1)
{
L[k]=l,R[k]=r,tag[k]=INF;;
if(l==r)
{
suma[k]=a[k]=sa.n-sa.sa[r-1];
sumh[k]=maxh[k]=minh[k]=sa.height[r-1];
return;
}
build(l,mid,lson);
build(mid+1,r,rson);
minh[k]=min(minh[lson],minh[rson]);
maxh[k]=max(maxh[lson],maxh[rson]);
sumh[k]=sumh[lson]+sumh[rson];
suma[k]=suma[lson]+suma[rson];
}
void pushdown(int k)
{
if(tag[k]==INF)return;
updh(L[lson],R[lson],tag[k],lson);
updh(L[rson],R[rson],tag[k],rson);
tag[k]=INF;
}
ll ask(int x,int y,int k=1)
{
if(x==L[k]&&y==R[k])return suma[k]-sumh[k];
pushdown(k);
ll ans=0;
if(x<=R[lson])ans+=ask(x,min(y,R[lson]),lson);
if(y>=L[rson])ans+=ask(max(x,L[rson]),y,rson);
return ans;
}
int geth(int x,int k=1)
{
if(L[k]==R[k])return maxh[k];
pushdown(k);
if(x<=R[lson])return geth(x,lson);
return geth(x,rson);
}
int getl(int x,int y,int &hei,int k=1)
{
if(L[k]==R[k]){hei=min(hei,minh[k]);return R[k]+(hei<y);}
pushdown(k);
if(x<=R[lson])return getl(x,y,hei,lson);
if(x>R[k]&&minh[k]>=y){hei=min(hei,minh[k]);return L[k];}
int ret=getl(x,y,hei,rson);
if(ret==L[rson])return getl(x,y,hei,lson);
return ret;
}
int getr(int x,int y,int &hei,int k=1)
{
if(L[k]==R[k]){hei=min(hei,minh[k]);return hei>=y?R[k]:(R[k]-1);}
pushdown(k);
if(x>=L[rson])return getr(x,y,hei,rson);
if(x<L[k]&&minh[k]>=y){hei=min(hei,minh[k]);return R[k];}
int ret=getr(x,y,hei,lson);
if(ret==R[lson])return getr(x,y,hei,rson);
return ret;
}
void seta(int x,int y,int k=1)
{
if(L[k]==R[k]){suma[k]=a[k]=min(a[k],y);return;}
pushdown(k);
if(x<=R[lson])seta(x,y,lson);
else seta(x,y,rson);
suma[k]=suma[lson]+suma[rson];
}
void updh(int x,int y,int z,int k=1)
{
if(x>y||z>=maxh[k])return;
if(L[k]==R[k])
{
sumh[k]=maxh[k]=minh[k]=min(minh[k],z);
suma[k]=a[k]=z;
return;
}
if(x==L[k]&&y==R[k])
{
tag[k]=min(tag[k],z);
minh[k]=maxh[k]=z;
sumh[k]=1ll*tag[k]*(y-x+1);
suma[k]=1ll*tag[k]*(y-x+1);
return;
}
pushdown(k);
if(x<=R[lson])updh(x,min(y,R[lson]),z,lson);
if(y>=L[rson])updh(max(x,L[rson]),y,z,rson);
minh[k]=min(minh[lson],minh[rson]);
maxh[k]=max(maxh[lson],maxh[rson]);
sumh[k]=sumh[lson]+sumh[rson];
suma[k]=suma[lson]+suma[rson];
}
}tree;
int solve()
{
int n=sa.init();
tree.build(1,n);
ll ans=0;
int q;
cin>>q;
while(q--)
{
int l,r;
scanf("%d%d",&l,&r);
int y=r-l+1;
l=sa.rk[l-1]+1;
int h=sa.height[l-1];
int L=tree.getl(l,y,h);
int R=l;
if(l<n)
{
h=sa.height[l];
R=tree.getr(l+1,y,h);
}
assert(L>=2);
assert(L-1<=R);
ans+=max(0ll,tree.ask(L-1,R)+tree.geth(L-1)-(y-1));
tree.seta(L-1,y-1);
if(L<=R)tree.updh(L,R,y-1);
printf("%lld ",ans);
}
return puts("");
}
int main()
{
int T=1;
cin>>T;
while(T--)solve();
return 0;
}