bzoj 4713: 迷失的字符串 (树形DP+bitset)

题目描述

传送门

题目大意:有一棵n个节点的大树,上面每条边有一个小写字符。
对于任意两个不同的点u,v,我们可以在树上找到u出发到v终止的唯一的一条最短路径,并将沿途经过的边上的字符依次写下来,得到一个字符串。
对于一个字符串,如果存在这样一个点对(u,v),使得它们路径上的字符串与其完全匹配,那么我们就称这个字符串属于这棵树。
现在有m个迷失的字符串,请你写一个程序帮助判断每一条字符串是否属于这棵树。

题解

对拍不出错也卡不住,交上去不知道为什么RE。。。。欢迎指正啊

说一下思路吧。
f[i][j] 表示从下往上走到达第i个点匹配到了字符串中第j个字符是否可行。
可以将所有的串连起来一起做,中间加一个分隔符,初始时所有点只有分隔符位置是1.
考虑 f[i][j] 可行的条件
(1)他的儿子中有 f[son][j1] =1
(2)son->i边上的字符与j相同
这两个条件都可以用位运算实现。
判断一个串是否出现过,只需要判断同一个点的上行和下行路径能否拼接行成即可。
我们用两个数组,一个从后向前推j+1->j,一个从前向后推j-1->j,如果最后j,j+1同为1,那么说明可以拼接。
用bitset加速即可

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<bitset>
#define N 30100
using namespace std;
bitset<N*2> f[N],g[N],tmpf,tmpg,base[30],ans,t;
int n,m,tot,point[N],v[N*2],c[N*2],nxt[N*2],l[N],r[N];
char s[N];
void add(int x,int y,int k)
{
    tot++; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;  c[tot]=k;
    tot++; nxt[tot]=point[y]; point[y]=tot; v[tot]=x;  c[tot]=k;
}
void dfs(int x,int fa)
{
    bool pd=false;
    for (int i=point[x];i;i=nxt[i]) {
        if(v[i]==fa) continue;
        dfs(v[i],x);
        pd=true;
        tmpf=(f[v[i]]<<1)&base[c[i]];
        tmpg=(g[v[i]]>>1)&base[c[i]];
        ans|=(f[x]<<1)&tmpg;
        ans|=tmpf&(g[x]>>1);
        f[x]|=tmpf; g[x]|=tmpg;
    }
}
int main()
{
    freopen("a.in","r",stdin);
    freopen("my.out","w",stdout);
    scanf("%d",&n);
    for (int i=1;i<n;i++) {
        int x,y; char s1[10];
        scanf("%d%d%s",&x,&y,s1);
        add(x,y,s1[0]-'a'+1);
    }
    scanf("%d",&m);
    r[0]=1;
    for (int i=1;i<=m;i++) {
      scanf("%s",s+1);
      int len=strlen(s+1);
      l[i]=r[i-1]+1; r[i]=r[i-1]+len;
      for (int j=1;j<=len;j++){
        int t=s[j]-'a'+1;
        base[t][j+r[i-1]]=1;
      }
      r[i]++;
    }
    for (int i=0;i<=m;i++) f[0][r[i]]=1; g[0]=f[0];
    for (int i=1;i<=n;i++) f[i]=g[i]=f[0];
    //for (int i=1;i<=r[m];i++) cout<<f[0][i]<<" "; cout<<endl;
    dfs(1,0);
    for (int i=1;i<=m;i++) {
        bool pd=false;
        for (int j=l[i];j<r[i];j++) pd|=ans[j];
        if (pd) printf("YES\n");
        else printf("NO\n");
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值