a180285幸运地被选做了地球到喵星球的留学生。他发现喵星人在上课前的点名现象非常有趣。
假设课堂上有N个喵星人,每个喵星人的名字由姓和名构成。喵星球上的老师会选择M个串来点名,每次读出一个串的时候,如果这个串是一个喵星人的姓或名的子串,那么这个喵星人就必须答到。
然而,由于喵星人的字码过于古怪,以至于不能用ASCII
码来表示。为了方便描述,a180285决定用数串来表示喵星人的名字。
现在你能帮助a180285统计每次点名的时候有多少喵星人答到,以及M次点名结束后每个喵星人答到多少次吗?
输入格式:
现在定义喵星球上的字符串给定方法:
先给出一个正整数L,表示字符串的长度,接下来L个整数表示字符串的每个字符。
输入的第一行是两个整数N和M。
接下来有N行,每行包含第i个喵星人的姓和名两个串。姓和名都是标准的喵星球上的字符串。
接下来有M行,每行包含一个喵星球上的字符串,表示老师点名的串。
输出格式:
对于每个老师点名的串输出有多少个喵星人应该答到。
然后在最后一行输出每个喵星人被点到多少次。
样例输入:
2 3
6 8 25 0 24 14 8 6 18 0 10 20 24 0
7 14 17 8 7 0 17 0 5 8 25 0 24 0
4 8 25 0 24
4 7 0 17 0
4 17 0 8 25
样例输出:
2
1
0
1 2
数据范围:
对于30%的数据,保证:
1<= N ,M <=1000 喵星人的名字总长不超过4000,点名串的总长不超过2000,
对于100%的数据,保证:
1<=N<=20000, 1<=M<=50000,喵星人的名字总长和点名串的总长分别不超过100000 保证喵星人的字符串中作为字符存在的数不超过10000
时间限制:
2S
空间限制:
128M
提示:
事实上样例给出的数据如果翻译成地球上的语言可以这样来看
2 3
izayoi sakuya
orihara izaya
izay
hara
raiz
本题是AC自动机模板题,注意点见下,第二次打,不太习惯。
#include<map>
#include<queue>
#include<algorithm>
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int n,m;
int u,v,w;
#define PER(i,a,b) for(int i=a;i<=b;i++)
vector<int> name[100001];
struct AK_automic_machine{
map<int,int>son[1000001];
map<int,int> ::iterator it;
int fail[1000001];
vector<int> num[1000001];
int ans1[20001],ans2[50001];
int vis[80001],used[50001];
int S,root,TAT,k;
void init()
{
S=1;root=1;//S must be 1.
for(int i=-233;i<=10000;i++) son[0][i]=1;//-233也要搞进去
memset(ans1,0,sizeof(ans1));
memset(ans2,0,sizeof(ans2));
}
void add(int x)
{
int p=root;
scanf("%d",&TAT);
PER(i,1,TAT)
{
scanf("%d",&k);
if(!son[p][k]) son[p][k]=++S;
p=son[p][k];
}
num[p].push_back(x);//push_back编号,别push_back长度
}
void AK_begin()
{
fail[root]=0;
queue<int>q;
int a,b,u,xx;
q.push(root);
while(!q.empty())
{
xx=q.front();q.pop();
for(it=son[xx].begin();it!=son[xx].end();it++)
{
a=it->first;
b=it->second;
u=fail[xx];
while(!son[u][a]) u=fail[u];
fail[b]=son[u][a];//md!要赋成son[u][a]
q.push(b);
}
}
}
void query(int x)
{
memset(vis,0,sizeof(vis));
memset(used,0,sizeof(used));
int p=root;
int temp=p;
int len=name[x].size();
PER(i,0,len-1)
{
while(!son[p][name[x][i]])p=fail[p];
//p更新完后,统计靠temp,自己也要更新。
p=son[p][name[x][i]];
temp=p;
while(temp)
{
if(vis[temp])break;
vis[temp]=1;
int kk=num[temp].size();
PER(j,0,kk-1)
{
if(!used[num[temp][j]])
{
used[num[temp][j]]=1;
ans1[x]++;
ans2[num[temp][j]]++;
}
}
temp=fail[temp];
}
}
}
}AC;
int main()
{
AC.init();
scanf("%d%d",&n,&m);
int len;
for(int i=1;i<=n;i++)
{
scanf("%d",&len);
PER(j,1,len)
{
scanf("%d",&v);name[i].push_back(v);
}
name[i].push_back(-233);
scanf("%d",&len);
PER(j,1,len)
{
scanf("%d",&v);name[i].push_back(v);
}
}
PER(i,1,m) AC.add(i);
AC.AK_begin();
PER(i,1,n) AC.query(i);
PER(i,1,m)printf("%d\n",AC.ans2[i]);
PER(i,1,n)printf("%d ",AC.ans1[i]);
}