喵星球上的点名

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]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值