DestinHistoire

 

BZOJ-1590 [Usaco2008 Dec]Secret Message 秘密信息(trie)

题目描述

  给出 \(n\)\(01\)\(s\)\(m\)\(01\)\(t\),对于每个字符串 \(t\),求有多少个字符串 $ s $ 满足 \(lcp(s,t)=\min(|s|,|t|)\)

  数据范围:\(1\leq n,m\leq 50000,1\leq |s|,|t|\leq 50000\)

分析

  把 \(n\) 个串 \(s\) 插入到 \(\text{trie}\) 中,在每条信息的结尾节点打上标记(\(ed[p]=1\)),并记录有多少条信息以此节点为结尾(\(num[p]++\))。在 \(t\) 串匹配的过程中,累加经过的每个节点的标记数 \(num\),最后加上以 \(t\) 串的结尾节点为根的子树的标记数之和 \(sum\)(也就是到达过这个节点且不以该节点为结尾的字符串的数量),这两部分加在一起就是答案。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
char s[N],t[N];
int trie[N][2],sum[N],ed[N],num[N],tot=1;
void insert(int len,char *s)
{
    int p=1;
    for(int i=0;i<len;i++)
    {
        int ch=s[i]-'0';
        if(trie[p][ch]==0)
            trie[p][ch]=++tot;
        sum[p]++;//到达过点p且不以p为结尾的标记数(也就是以p为根节点的子树的标记数)
        p=trie[p][ch];
    }
    ed[p]=1;
    num[p]++;
}
int query(int len,char *t)
{
    int p=1,ans=0;
    for(int i=0;i<len;i++)
    {
        int ch=t[i]-'0';
        if(trie[p][ch]==0)//失配直接范围路径上的标记数之和
            return ans;
        p=trie[p][ch];
        if(ed[p]==1)
            ans=ans+num[p];
    }
    return ans+sum[p];
}
int main()
{
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        int len;
        scanf("%d",&len);
        for(int i=0;i<len;i++)
        {
            int x;
            scanf("%d",&x);
            s[i]=char(x+'0');
        }
        insert(len,s);
    }
    for(int i=1;i<=m;i++)
    {
        int len;
        scanf("%d",&len);
        for(int i=0;i<len;i++)
        {
            int x;
            scanf("%d",&x);
            t[i]=(char)(x+'0');
        }
        printf("%d\n",query(len,t));
    }
    return 0;
}

posted on 2020-11-24 15:07  DestinHistoire  阅读(94)  评论(0)    收藏  举报

导航