【题目】
Codechef
给定一个字符串
S
S
S和
Q
Q
Q个询问,每个询问形如
(
l
i
,
p
i
)
(l_i,p_i)
(li,pi),回答有多少个长度为
l
i
l_i
li的字符串在
S
S
S中恰好出现了
p
i
p_i
pi次。
∣
S
∣
≤
2
×
1
0
5
,
Q
≤
5
×
1
0
5
|S|\leq 2\times 10^5,Q\leq 5\times 10^5
∣S∣≤2×105,Q≤5×105
【解题思路】
字符串题,不妨先建出
SAM
\text{SAM}
SAM。
我们知道 SAM \text{SAM} SAM上的每个节点代表了长度为一段区间的字符串,而 right \text{right} right集合的大小即这些字符串的出现次数。
于是我们不妨在一个二维平面上表示出来,纵坐标代表字符串长度,横坐标代表出现次数,那么每个节点就对应二维平面上一条垂直 x x x轴的线段。对于每个询问,即求一个点 ( p i , l i ) (p_i,l_i) (pi,li)被多少条线段覆盖过。
这就是一个经典的扫描线问题。(而且只是线段就更舒服了,值域又这么小,直接打个标记 BIT \text{BIT} BIT维护就行了)
复杂度 O ( n log n ) O(n\log n) O(nlogn)
【参考代码】
#include<bits/stdc++.h>
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
using namespace std;
typedef pair<int,int> pii;
const int N=5e5+10;
int read()
{
int ret=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
return ret;
}
void write(int x){if(x>9)write(x/10);putchar(x%10^48);}
void writeln(int x){write(x);putchar('\n');}
namespace SAM
{
int b[N],c[N];
struct SAM
{
int sz,las,ch[N][26],fa[N],siz[N],mx[N];
void init(){las=sz=1;}
void extend(int x)
{
int p,q,np,nq;
p=las;las=np=++sz;mx[np]=mx[p]+1;++siz[las];
for(;p && !ch[p][x];p=fa[p]) ch[p][x]=np;
if(!p) fa[np]=1;
else
{
q=ch[p][x];
if(mx[q]==mx[p]+1) fa[np]=q;
else
{
nq=++sz;mx[nq]=mx[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
fa[nq]=fa[q];fa[q]=fa[np]=nq;
for(;p && ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
}
}
}
void merge()
{
for(int i=1;i<=sz;++i) b[mx[i]]++;
for(int i=1;i<=sz;++i) b[i]+=b[i-1];
for(int i=sz;i;--i) c[b[mx[i]]--]=i;
for(int i=sz,x;i;--i) x=c[i],siz[fa[x]]+=siz[x];
//for(int i=2;i<=sz;++i) printf("%d %d %d %d %d\n",i,fa[i],siz[i],mx[i],mx[fa[i]]);
}
}S;
}
using namespace SAM;
namespace DreamLolita
{
int n,Q;
int ans[N];
char s[N];
vector<pii>vec[N],qr[N];
struct BIT
{
#define lowbit(x) (x&(-x))
int c[N];
void up(int x,int v){for(;x<N;x+=lowbit(x))c[x]+=v;}
void update(int x,int y,int v){up(x,v);up(y+1,-v);}
int query(int x){int res=0;for(;x;x-=lowbit(x))res+=c[x];return res;}
#undef lowbit
}bit;
void solve()
{
scanf("%s",s+1);n=strlen(s+1);S.init();
for(int i=1;i<=n;++i) S.extend(s[i]-'a');
S.merge();
for(int i=2;i<=S.sz;++i) vec[S.siz[i]].pb(mkp(S.mx[S.fa[i]]+1,S.mx[i]));
Q=read();
for(int i=1,l,p;i<=Q;++i) l=read(),p=read(),qr[p].pb(mkp(l,i));
for(int i=1;i<=n;++i)
{
if(!qr[i].size() || !vec[i].size()) continue;
for(auto j:vec[i]) bit.update(j.fi,j.se,1);
for(auto j:qr[i]) ans[j.se]=bit.query(j.fi);
for(auto j:vec[i]) bit.update(j.fi,j.se,-1);
}
for(int i=1;i<=Q;++i) writeln(ans[i]);
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("CC_SUBQUERY.in","r",stdin);
freopen("CC_SUBQUERY.out","w",stdout);
#endif
DreamLolita::solve();
return 0;
}