题目大意:
题目链接:https://www.luogu.org/problemnew/show/P2922
给出
n
n
n个01串
a
a
a,再给出
m
m
m个01串
b
b
b,求对于每一个
b
b
b串,有多少
a
a
a串和这个
b
b
b串有相同的前缀。
思路:
这道题考
T
r
i
e
Trie
Trie。
对于每一个结点,最多有两个子节点(0和1),那么可以先把所有
a
a
a串插入到
T
r
i
e
Trie
Trie里,然后对于每一个
b
b
b串,我们在
T
r
i
e
Trie
Trie里面匹配,当两种情况时结束:
- 当无法再往下匹配时(没有子结点了)。此时可以直接输出答案。
- 当长度不够期且下面还有结点时。此时答案为 a n s − e n d [ p ] + s u m [ p ] ans-end[p]+sum[p] ans−end[p]+sum[p]。因为有还有 s u m [ p ] sum[p] sum[p]个串经过这个结点,所以就有 s u m [ p ] sum[p] sum[p]个串的前缀和它的前缀一样。
代码:
#include <cstdio>
using namespace std;
int n,m,k,ans,a[50011],sum[500011],end[500011],tot=1,trie[500001][2];
void insert(int len) //插入
{
int p=1;
for (int i=1;i<=len;i++)
{
if (!trie[p][a[i]]) trie[p][a[i]]=++tot; //新建节点
p=trie[p][a[i]];
sum[p]++; //经过该结点的串的个数
}
end[p]++; //结束位置
}
void find(int len) //查找
{
int p=1;
for (int i=1;i<=len;i++)
{
if (!trie[p][a[i]]) //没得往下找了
{
printf("%d\n",ans);
return;
}
p=trie[p][a[i]];
ans+=end[p]; //加上答案
}
printf("%d\n",ans-end[p]+sum[p]);
return;
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
{
scanf("%d",&k);
for (int j=1;j<=k;j++)
scanf("%d",&a[j]);
insert(k);
}
for (int i=1;i<=m;i++)
{
scanf("%d",&k);
for (int j=1;j<=k;j++)
scanf("%d",&a[j]);
ans=0;
find(k);
}
return 0;
}