【SCOI2012】喵星球上的点名

31 篇文章 0 订阅
5 篇文章 0 订阅

【SCOI2012】喵星球上的点名

Description

有n个串,代表n个人的姓氏和名字,都是用很多个数字表示的,比如我姓1,2,3,4,名4,5,6,7。然后有m个点名串,如果点到了某个人的姓或名里面的某一串,那个人就被点到,不过一个人在一个点名串中只能被点一次。比如点名串是2,3,4,我的姓中含有2,3,4,那么我就会被叫到。求每个学生分别被叫到多少次,和每个点名串分别叫到多少个学生。

Solution

这一题,很明显是用一个个的姓氏和名字在点名树中匹配,那么用到的算法明显是ac_automation(ac自动机)。那么为什么不是用一个个点名串在姓名树中匹配呢——>因为如果这样做,那么就有一颗姓树和一颗名树,明显比建造点名树要麻烦的多。然后套了ac自动机的模板,还要考虑一件事(这个坑了我好久,我还以为没有重复串),点名串可能有重复串(不用管姓名串了,因为我们是逐个匹配),如果有重复的姓名串,那么还要用一个数组存此节点含有那些点名串,然后统计答案。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<map>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
char s[10005];
bool bz[100005],cz[100005];
map<int,int> trie[100005];
int i,j,k,l,t,n,m,tot,x,num,da;
vector<int> a[1000005],b,c,d[100005],biao[1000005];
int data[1000005],ans[1000005],ans1[1000005],next[1000005],sum[1000005];
void build_ac_automation(int root){
    int i,now,head=0,tail=1;
    data[1]=root;
    while(head<=tail){
        now=data[++head];
        for(map<int,int>:: iterator ii=trie[now].begin();ii!=trie[now].end();ii++){
            i=ii->first;
            if(trie[now][i]==0)continue;
            int x=trie[now][i];
            if(now>1){
                j=next[now];
                while(j>=1&&trie[j][i]==0) j=next[j];
                if(j<1) next[x]=1;
                else next[x]=trie[j][i];
            }
            if(now==1) next[x]=1;
            data[++tail]=x;
        }
    }
}
int main(){
    scanf("%d%d",&n,&m);
    fo(i,1,n){
        scanf("%d",&l);
        fo(j,1,l){
            scanf("%d",&t);
            a[i].push_back(t);
        }
        a[i].push_back(-1);
        scanf("%d",&l);
        fo(j,1,l){
            scanf("%d",&t);
            a[i].push_back(t);
        }
    }
    num=1;
    fo(i,1,m){
        scanf("%d",&l);
        x=1;
        fo(j,1,l){
            scanf("%d",&t);
            if(trie[x][t]==0){
                trie[x][t]=++num;
                x=trie[x][t];
            }
            else x=trie[x][t];
        }
        biao[x].push_back(i);
    }
    build_ac_automation(1);
    fo(l,1,n){
        x=1;
        for(i=0;i<a[l].size();i++){
            int u=a[l][i];
            while(x&&!trie[x][u])x=next[x];
            x=trie[x][u];
            if(x==0) x=1;
            for(j=x;j;j=next[j]){
                for(k=0;k<biao[j].size();k++){
                    if(!cz[biao[j][k]]){
                        cz[biao[j][k]]=1;
                        c.push_back(biao[j][k]);
                        ans[biao[j][k]]++;
                        ans1[l]++;
                    }
                }
            }
        }
        for(i=0;i<c.size();i++) cz[c[i]]=0;
        c.clear();b.clear();
    }
    fo(i,1,m) printf("%d\n",ans[i]);
    fo(i,1,n) printf("%d ",ans1[i]);
}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值