题目:
题意:
n
n
n个人的名字构造形式如一棵字典树,给定一个前缀
s
s
s,问有多少个人的名字的前缀是
s
s
s
共有
q
q
q组询问
分析:
因为前缀是我们所陌生的,但后缀我们熟呀,就把整个树倒过来,就变成求后缀了
问后缀为
s
s
s的有多少个,如果我们将每个名字都标号并且使得其有序的话,就变成要求出编号最小
(
l
)
(l)
(l)的后缀为
s
s
s的哪个,以及最大的
(
r
)
(r)
(r),最后答案就是
r
−
l
+
1
r-l+1
r−l+1
基于这样的思路,我们考虑将树上的名字也就是字符串排序,后缀数组能在
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)的时间轻松解决
l
,
r
l,r
l,r则用二分来确定位置
代码:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#define LL long long
using namespace std;
inline LL read() {
LL d=0,f=1;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){d=d*10+s-'0';s=getchar();}
return d*f;
}
int n,m=30,q,cnt;
int sa[1000005],sa2[1000005],rk[1000005],rk2[1000005],tp[1000005],c[1000005];
int fa[1000005][21],ch[1000005],z[1000005],dep[1000005];
void Rsort(int *rk,int *sa)
{
for(int i=0;i<=m;i++) c[i]=0;
for(int i=1;i<=n;i++) c[rk[tp[i]]]++;
for(int i=1;i<=m;i++) c[i]+=c[i-1];
for(int i=n;i>=1;i--) sa[c[rk[tp[i]]]--]=tp[i];
return;
}
void SA()
{
for(int i=1;i<=n;i++) rk[i]=ch[i],tp[i]=i;
Rsort(rk,sa);
for(int k=1,w=0;k<n;k<<=1,w++)
{
int j=0;
for(int i=1;i<=n;i++) rk2[i]=rk[fa[i][w]];
for(int i=1;i<=n;i++) tp[i]=i;
Rsort(rk2,sa2);
for(int i=1;i<=n;i++) tp[i]=sa2[i];
Rsort(rk,sa);
swap(rk,tp);rk[sa[1]]=j=1;
for(int i=2;i<=n;i++)
rk[sa[i]]=(tp[sa[i]]==tp[sa[i-1]]&&tp[fa[sa[i]][w]]==tp[fa[sa[i-1]][w]]?j:++j);
m=j;
}
for(int i=1;i<=n;i++) rk[sa[i]]=i;
return;
}
int check(int x)
{
int len=min(cnt,dep[sa[x]]),k=sa[x];
for(int i=1;i<=len;i++)
{
if(ch[k]<z[i]) return -1;
if(ch[k]>z[i]) return 1;
k=fa[k][0];
}
if(len==cnt) return 0;
return -1;
}
void solve(int len)
{
int L=n+1,R=0,l=1,r=n;
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid)>=0) L=mid,r=mid-1;
else l=mid+1;
}
l=1,r=n;
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid)<=0) R=mid,l=mid+1;
else r=mid-1;
}
printf("%d\n",R-L+1);
return;
}
int main()
{
n=read();q=read();
dep[1]=1;
for(int i=1;i<=n;i++)
{
char c=getchar();
while(c<'A'||c>'Z') c=getchar();
int x=read();
ch[i]=c-'A'+1;
fa[i][0]=x;dep[i]=dep[x]+1;
for(int j=1;j<21;j++) fa[i][j]=fa[fa[i][j-1]][j-1];
}
SA();
while(q--)
{
char c=getchar();
while(c<'A'||c>'Z') c=getchar();
cnt=0;
while(c>='A'&&c<='Z') z[++cnt]=c-'A'+1,c=getchar();
solve(cnt);
}
return 0;
}