题意
给出一个字符构成的树,每个节点到根经过的字符组成一个字符串。有多个询问,每次询问一个字符串,求询问串是多少个字符串的前缀
题解1 树上SA
在树上求后缀数组,求出每个字符串的字典序的排名
对于询问操作,找出询问串在字符串中的排名的区间范围,就得出答案了
由于是有序的,所以可以用二分查找,前缀等于询问串的最小和最大的排名,差值就是答案
注意二分查找时的边界问题:
L
a
=
n
+
1
,
R
a
=
0
La=n+1,Ra=0
La=n+1,Ra=0
这样,在
R
a
−
L
a
+
1
Ra-La+1
Ra−La+1 时,可以解决答案为0的情况
树上后缀数组
代码
#include<bits/stdc++.h>
#define ll unsigned long long
#define N 1000010
using namespace std;
const int base=31;
int n,m=30,q,cnt;
int sa[N],sa2[N],rk[N],rk2[N],dep[N],tp[N],c[N],fa[N][21],ch[N],la[N],ls[N];
inline 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];
}
inline void build_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;
}
inline int cmp(int x){
int tot=min(cnt,dep[sa[x]]),k=sa[x];
for(int i=1;i<=tot;i++){
if (ch[k]<ls[i]) return -1;
if (ch[k]>ls[i]) return 1;
k=fa[k][0];
}
if (tot==cnt) return 0;
return -1;
}
void solve(int len){
int La=n+1,Ra=0,l=1,r=n;
while(l<=r){
int t=l+r>>1;
if (cmp(t)>=0) La=t,r=t-1;else
l=t+1;
}
l=1,r=n;
while(l<=r){
int t=l+r>>1;
if (cmp(t)<=0) Ra=t,l=t+1;else
r=t-1;
}
printf("%d\n",Ra-La+1);
}
int main(){
scanf("%d%d",&n,&q);
getchar(); dep[1]=1;
for(int i=1,x;i<=n;i++){
char c=getchar(); scanf("%d",&x);getchar();
ch[i]=c-'A'+2;
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];
}
build_SA();
while(q--){
char c=getchar();
cnt=0;
while(c!='\n') ls[++cnt]=c-'A'+2,c=getchar();
solve(cnt);
}
return 0;
}
题解2 AC自动机
将询问串反向插入自动机中,然后按照字典树的dfs序列在自动机上匹配
注意,匹配成功时,那么
f
a
i
l
fail
fail 树的子树都匹配成功,统计时,要根据拓扑序求答案
方法可以参考 洛谷P5357的题解
代码
#include<bits/stdc++.h>
#define N 1000010
#define INF 0x3f3f3f3f
#define eps 1e-6
#define pi acos(-1.0)
#define mod 998244353
#define P 1000000007
#define LL long long
#define pb push_back
#define fi first
#define se second
#define cl clear
#define si size
#define lb lower_bound
#define ub upper_bound
#define bug(x) cerr<<#x<<" : "<<x<<endl
#define mem(x) memset(x,0,sizeof x)
#define sc(x) scanf("%d",&x)
#define scc(x,y) scanf("%d%d",&x,&y)
#define sccc(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
int w[N],in[N],id[N],n,q,ch[N],ls[N];
int la[N],Q[N];
struct ege{int u,nxt; }G[N]; int cnt;
inline void add(int u,int v){
G[++cnt]={v,la[u]}; la[u]=cnt;
}
struct Trie{
int t[N][26],fail[N];
int tot;
void ins(int g){
int i=0;
for(int j=cnt;j>=1;j--){
int c=ls[j];
if(!t[i][c]) t[i][c]=++tot;
i=t[i][c];
}
id[g]=i;
}
void get_fail(){
int h=0,tail=0;
for (int i=0;i<26;i++) if (t[0][i]) Q[++tail]=t[0][i];
while(h<tail) {
int i=Q[++h];
for(int j=0;j<26;j++) {
if (t[i][j]){
fail[t[i][j]]=t[fail[i]][j];
Q[++tail]=t[i][j];
}else
t[i][j]=t[fail[i]][j];
}
}
}
void AC_automation(int x,int pre){
for(int i=la[x];i;i=G[i].nxt){
w[t[pre][ch[G[i].u]]]++;
AC_automation(G[i].u,t[pre][ch[G[i].u]]);
}
}
void work(){
for(int i=1;i<=tot;i++) in[fail[i]]++;
int h=0,tail=0;
for(int i=1;i<=tot;i++) if(!in[i]) Q[++tail]=i;
while(h<tail){
int x=Q[++h];
int i=fail[x];
w[i]+=w[x];
in[i]--;
if (!in[i]) Q[++tail]=i;
}
}
}T1;
char t[N],s[N];
int main(){
scanf("%d%d",&n,&q); getchar();
for(int i=1,x;i<=n;i++){
char c=getchar(); scanf("%d",&x);getchar();
ch[i]=c-'A';
add(x,i);
}
for(int i=1;i<=q;i++){
char c=getchar();
cnt=0;
while(c!='\n') ls[++cnt]=c-'A',c=getchar();
T1.ins(i);
}
T1.get_fail();
T1.AC_automation(0,0);
T1.work();
for(int i=1;i<=q;i++) printf("%d\n",w[id[i]]);
}
题解3 广义后缀自动机
BFS 建立广义后缀自动机,求出每个状态的 s i z e size size,拿提问串倒序在自动机上跑,结束时到达的状态的 s i z e size size就是答案
代码
#pragma GCC optimize(3)//手动Ox优化
#include<bits/stdc++.h>
#define N 2000010
#define inf 0x3f3f3f3f
#define eps 1e-5
#define pi 3.141592653589793
#define mod 998244353
#define P 1000000007
#define LL long long
#define pb push_back
#define fi first
#define se second
#define cl clear
#define si size
#define lb lower_bound
#define ub upper_bound
#define bug(x) cerr<<#x<<" : "<<x<<endl
#define mem(x,y) memset(x,0,sizeof(int)*(y+3))
#define sc(x) scanf("%d",&x)
#define scc(x,y) scanf("%d%d",&x,&y)
#define sccc(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
int n, k, fa;
char s[N];
string q;
vector<int> trie[N];
struct SAM {
int ch[N][26],fa[N],len[N],last[N],a[N],t[N],size[N],cnt=1,root=1;
int add(int p, int c) {
if(ch[p][c]&&len[ch[p][c]]==len[p]+1) return ch[p][c];
int np = ++cnt,fg=0;
size[np] = 1; len[np] = len[p] + 1;
while (!ch[p][c] && p) ch[p][c]=np,p=fa[p];
if (!p) {
fa[np] = root;
}else{
int q = ch[p][c];
if (len[p]+1==len[q]) {
fa[np]=q;
}else{
if (len[p]+1==len[np]) fg=1;
int nq = ++cnt;
memcpy(ch[nq], ch[q], sizeof(ch[q]));
len[nq]=len[p]+1;
fa[nq]=fa[q];
fa[q]=fa[np]=nq;
while (p&&ch[p][c]==q) ch[p][c]=nq,p=fa[p];
return fg?nq:np;
}
}
return np;
}
void init() {
//如多次建立自动机,加入memset操作
root = cnt = 1;
}
void build() {
init();
queue<int> q;
q.push(0), last[0] = root;
while (!q.empty()) {
int p=q.front(); q.pop();
for (int i=0;i<trie[p].size();i++) {
last[trie[p][i]] = add(last[p], s[trie[p][i]] - 'A');
q.push(trie[p][i]);
}
}
for (int i=1;i<=cnt;i++) {t[len[i]]++; }
for (int i=1;i<=cnt;i++) {t[i]+=t[i - 1]; }
for (int i=1;i<=cnt;i++) {a[t[len[i]]--] = i; }
for (int i=cnt;i>root;i--) size[fa[a[i]]] += size[a[i]];
}
void solve(){
int p=1;
for(int i=q.length()-1;i>=0;i--){
int c=q[i]-'A';
if (!ch[p][c]){
p=0; break;
}
p=ch[p][c];
}
cout<<size[p]<<'\n';
}
}SAM;
int main() {
ios::sync_with_stdio(0);
cin >> n >> k;
for (int i = 1; i <= n; ++i) {
cin >> s[i] >> fa;
trie[fa].push_back(i);
}
SAM.build();
while (k--) {
cin >> q;
SAM.solve();
}
return 0;
}