首先考虑树上差分, x 到 y 的答案可以差分成 x 到根+ y 到根- 2∗lca(x,y) 到根的答案。
考虑如何维护每个点到根路径上的所有字符串,想到Trie树,又因为每个点和其父亲只相差一个串,每个串长又不超过 10 ,所以考虑用可持久化Trie树,这样树上节点就不会超过所有串总长。树上每个点记录 size 表示在其子树中有多少个串。
每次询问的时候只要在Trie上跑完询问串,当前点的 size 就是答案。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=100010;
int n,q,sz[maxn*10],trie[maxn*10][26],rt[maxn],fa[maxn][20],d[maxn],tot;
char sc[15];
struct edge
{
int t,l;
char s[15];
edge *next;
}*con[maxn];
void ins(int x,int y)
{
edge *p=new edge;
p->t=y;
p->l=strlen(sc+1);
for(int i=1;i<=p->l;i++)
p->s[i]=sc[i];
p->next=con[x];
con[x]=p;
}
void build(int now,int lst,edge *p,int d)
{
sz[now]=sz[lst]+1;
if(d>p->l) {return ;}
for(int c=0;c<=25;c++)
if(c!=p->s[d]-'a') trie[now][c]=trie[lst][c];
trie[now][p->s[d]-'a']=++tot;
build(tot,trie[lst][p->s[d]-'a'],p,d+1);
}
void init()
{
for(int k=1;(1<<k)<=n;k++)
for(int i=1;i<=n;i++)
fa[i][k]=fa[fa[i][k-1]][k-1];
}
int lca(int x,int y)
{
if(d[x]<d[y]) swap(x,y);
for(int k=19;k>=0;k--) if(d[fa[x][k]]>=d[y]) x=fa[x][k];
for(int k=19;k>=0;k--) if(fa[x][k]!=fa[y][k]) x=fa[x][k],y=fa[y][k];
return (x==y?x:fa[x][0]);
}
void dfs(int v)
{
d[v]=d[fa[v][0]]+1;
for(edge *p=con[v];p;p=p->next)
if(p->t!=fa[v][0])
{
rt[p->t]=++tot;
build(rt[p->t],rt[v],p,1);
fa[p->t][0]=v;
dfs(p->t);
}
}
int qry(int p)
{
int len=strlen(sc+1);
for(int i=1;i<=len;i++)
p=trie[p][sc[i]-'a'];
return sz[p];
}
int main()
{
scanf("%d",&n);
for(int i=1;i<n;i++)
{
int x,y,len;
scanf("%d%d%s",&x,&y,sc+1);
ins(x,y);
ins(y,x);
}
rt[1]=tot=1;
dfs(1);
init();
scanf("%d",&q);
while(q--)
{
int x,y;
scanf("%d%d%s",&x,&y,sc+1);
printf("%d\n",qry(rt[x])+qry(rt[y])-2*qry(rt[lca(x,y)]));
}
return 0;
}