[bzoj4477][可持久化线段树][哈希表]字符串树

本文介绍了一种基于字符串树的数据结构处理方法,通过构建可持久化线段树来高效解决字符串匹配问题,特别是在求解特定字符串作为前缀出现次数的任务中。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

萌萌买了一颗字符串树的种子,春天种下去以后夏天就能长出一棵很大的字 符串树。字符串树很奇特,树枝上都密密麻麻写满了字符串,看上去很复杂的样
子。 【问题描述】 字符串树本质上还是一棵树,即N个节点N-1条边的连通无向无环图,节点
从1到N编号。与普通的树不同的是,树上的每条边都对应了一个字符串。萌萌
和JYY在树下玩的时候,萌萌决定考一考JYY。每次萌萌都写出一个字符串S和
两个节点U,V,需要JYY立即回答U和V之间的最短路径(即,之间边数最少的
路径。由于给定的是一棵树,这样的路径是唯一的)上有多少个字符串以为前 缀。
JYY虽然精通编程,但对字符串处理却不在行。所以他请你帮他解决萌萌的难题。

Input

输入第一行包含一个整数N,代表字符串树的节点数量。 接下来N-1行,每行先是两个数U,V,然后是一个字符串S,表示节点和U节
点V之间有一条直接相连的边,这条边上的字符串是S。输入数据保证给出的是一 棵合法的树。 接下来一行包含一个整数Q,表示萌萌的问题数。
接来下Q行,每行先是两个数U,V,然后是一个字符串S,表示萌萌的一个问 题是节点U和节点V之间的最短路径上有多少字符串以S为前缀。
输入中所有字符串只包含a-z的小写字母。 1<=N,Q<=100,000,且输入所有字符串长度不超过10。

Output

输出Q行,每行对应萌萌的一个问题的答案。

Sample Input

4

1 2 ab

2 4 ac

1 3 bc

3

1 4 a

3 4 b

3 2 ab

Sample Output

2

1

1

题解

看到长度<=10那就直接上哈希嘛
对每一个长度都建一棵可持久化线段树,保存这个长度的哈希值。
然后日常线段树上树
询问的时候就把串的哈希搞出来,找到LCA。由于这里我们是在边上搞字符串,所以减的时候减去2倍LCA。在询问串的长度这棵线段树里询问即可

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
unsigned int HA=119;
struct trnode
{
    int lc,rc,c;
}tr[21110000];int trlen;
void add(int &now,int l,int r,int p)
{
    if(now==0)now=++trlen;
    tr[now].c++;
    if(l==r)return ;
    int mid=(l+r)/2;
    if(p<=mid)add(tr[now].lc,l,mid,p);
    else add(tr[now].rc,mid+1,r,p);
}
int rt[11][110000];
void merge(int &x,int y)
{
    if(x==0){x=y;return ;}
    if(y==0)return ;
    tr[x].c+=tr[y].c;
    merge(tr[x].lc,tr[y].lc);
    merge(tr[x].rc,tr[y].rc);
}
int findKth(int x,int y,int u,int l,int r,int p)
{
    if(tr[x].c+tr[y].c-2*tr[u].c==0)return 0;
    if(l==r)return tr[x].c+tr[y].c-2*tr[u].c;
    int lcx=tr[x].lc,rcx=tr[x].rc,lcy=tr[y].lc,rcy=tr[y].rc,lcu=tr[u].lc,rcu=tr[u].rc;
    int mid=(l+r)/2;
    if(p<=mid)return findKth(lcx,lcy,lcu,l,mid,p);
    else return findKth(rcx,rcy,rcu,mid+1,r,p);
}
struct node
{
    int x,y,next;
    char ch[15];
}a[210000];int len,last[110000];
void ins(int x,int y)
{
    len++;
    a[len].x=x;a[len].y=y;
    a[len].next=last[x];last[x]=len;
}
int n,m,bin[25],fa[110000][25],dep[110000];
struct LSnode{unsigned int y;int p;}w[11][110000];int Rank[11][110000],tt[11];
bool cmp(LSnode n1,LSnode n2){return n1.y<n2.y;}
void pre_tree_node(int x)
{
    for(int i=1;bin[i]<=dep[x];i++)fa[x][i]=fa[fa[x][i-1]][i-1];
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if(y!=fa[x][0])
        {
            fa[y][0]=x;
            dep[y]=dep[x]+1;
            int len=strlen(a[k].ch+1);
            unsigned int tmp=0;
            for(int i=1;i<=len;i++)
            {
                tmp=tmp*HA+a[k].ch[i]-'a';
                w[i][y].y=tmp;w[i][y].p=y;
            }
            pre_tree_node(y);
        }
    }
}
void dfs(int x)
{
    for(int i=1;i<=10;i++)
    {
        add(rt[i][x],1,tt[i],Rank[i][x]);
        merge(rt[i][x],rt[i][fa[x][0]]);
    }
    for(int k=last[x];k;k=a[k].next)if(a[k].y!=fa[x][0])dfs(a[k].y);
}
int lca(int x,int y)
{
    if(dep[x]<dep[y])swap(x,y);
    for(int i=20;i>=0;i--)if(bin[i]<=dep[x] && dep[fa[x][i]]>=dep[y])x=fa[x][i];
    if(x==y)return x;
    for(int i=20;i>=0;i--)if(bin[i]<=dep[x] && fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}
int fd(int op,unsigned int tmp)
{
    int l=1,r=n;
    while(l<=r)
    {
        int mid=(l+r)/2;
        if(w[op][mid].y<tmp)l=mid+1;
        else if(w[op][mid].y>tmp)r=mid-1;
        else return Rank[op][w[op][mid].p];
    }
    return -1;
}
char st[15];
int main()
{
    bin[0]=1;for(int i=1;i<=20;i++)bin[i]=bin[i-1]*2;
    scanf("%d",&n);
    for(int i=1;i<n;i++)
    {
        int x,y;scanf("%d%d",&x,&y);
        scanf("%s",a[len+1].ch+1);
        ins(x,y);ins(y,x);
        int cnt=strlen(a[len-1].ch+1);
        for(int j=1;j<=cnt;j++)a[len].ch[j]=a[len-1].ch[j];
    }
    fa[1][0]=0;dep[1]=1;
    pre_tree_node(1);
    for(int i=1;i<=10;i++)
    {
        sort(w[i]+1,w[i]+1+n,cmp);
        Rank[i][w[i][1].p]=1;tt[i]=1;
        for(int j=2;j<=n;j++)
        {
            if(w[i][j].y!=w[i][j-1].y)tt[i]++;
            Rank[i][w[i][j].p]=tt[i];
        }
    }
    dfs(1);
    scanf("%d",&m);
    while(m--)
    {
        int u,v;scanf("%d%d%s",&u,&v,st+1);
        int len=strlen(st+1);
        unsigned int tmp=0;
        for(int i=1;i<=len;i++)tmp=tmp*HA+st[i]-'a';
        int cnt=fd(len,tmp);
        if(cnt==-1)printf("0\n");
        else
        {
            int LT=lca(u,v);
            printf("%d\n",findKth(rt[len][u],rt[len][v],rt[len][LT],1,tt[len],cnt));
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值