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) 收藏 举报
浙公网安备 33010602011771号