BZOJ 2754: [SCOI2012]喵星球上的点名 [AC自动机+map+暴力]

2754: [SCOI2012]喵星球上的点名

Time Limit: 20 Sec  Memory Limit: 128 MB
Submit: 1902  Solved: 837
[Submit][Status][Discuss]

Description

a180285幸运地被选做了地球到喵星球的留学生。他发现喵星人在上课前的点名现象非常有趣。   假设课堂上有N个喵星人,每个喵星人的名字由姓和名构成。喵星球上的老师会选择M个串来点名,每次读出一个串的时候,如果这个串是一个喵星人的姓或名的子串,那么这个喵星人就必须答到。 然而,由于喵星人的字码过于古怪,以至于不能用ASCII码来表示。为了方便描述,a180285决定用数串来表示喵星人的名字。
现在你能帮助a180285统计每次点名的时候有多少喵星人答到,以及M次点名结束后每个喵星人答到多少次吗?  

Input

现在定义喵星球上的字符串给定方法:
先给出一个正整数L,表示字符串的长度,接下来L个整数表示字符串的每个字符。
输入的第一行是两个整数N和M。
接下来有N行,每行包含第i 个喵星人的姓和名两个串。姓和名都是标准的喵星球上的
字符串。
接下来有M行,每行包含一个喵星球上的字符串,表示老师点名的串。

Output

对于每个老师点名的串输出有多少个喵星人应该答到。
然后在最后一行输出每个喵星人被点到多少次。

HINT

【数据范围】 

对于30%的数据,保证: 

1<=N,M<=1000,喵星人的名字总长不超过4000,点名串的总长不超过2000。

对于100%的数据,保证:

1<=N<=20000,1<=M<=50000,喵星人的名字总长和点名串的总长分别不超过100000,保证喵星人的字符串中作为字符存在的数不超过10000。


喵~ http://www.cnblogs.com/candy99/p/cat.html

 


 

首先想到两问建两个AC自动机,第一问是把喵名建自动机然后点名串统计fail子树,但是不对啊啊啊啊啊人家一个喵就算一次.....


所以就发现其他人用了个很暴力的方法,点名串建AC自动机然后喵在上面跑,每跑一个字符暴力统计更新fail指向的点名串......然后就过了

一些细节:

1.要用map  字符集太大(离散化并没有用) 这样建AC自动机的时候就不能用Trie图优化了用普通做法

2.一个喵统计点名串需要用个vis[],从黄学长哪里学到了黑科技用个vector记录vis了那些,不用memset直接把vector里的还原行了

3.可能有重复点名串!!!!!

4.姓和名用个特殊数字连起来行了 

 

980ms

 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
using namespace std;
const int N=1e5+5,INF=1e9;
inline int read(){
    char c=getchar();int x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();}
    return x*f;
}

int n,m,k;
vector<int> a[N];
struct node{
    map<int,int> ch;
    int fail,val;
    vector<int> s;
}t[N];
int sz;
void ins(int id){
    int u=0,n=read();
    for(int i=1;i<=n;i++){
        int c=read();
        if(!t[u].ch[c]) t[u].ch[c]=++sz;
        u=t[u].ch[c];
    }
    t[u].s.push_back(id);
}
int q[N],head,tail;
map<int,int>::iterator it,it2;
void getAC(){
    head=tail=1;
    for(it=t[0].ch.begin();it!=t[0].ch.end();it++)
        q[tail++]=it->second;
    while(head!=tail){
        int u=q[head++];
        for(it=t[u].ch.begin();it!=t[u].ch.end();it++){
            int v=it->second,c=it->first;
            int now=t[u].fail;
            while(now&&!t[now].ch.count(c)) now=t[now].fail;
            if(t[now].ch.count(c)) t[v].fail=t[now].ch[c];
            else t[v].fail=now;//0
            q[tail++]=v;
        }
    }
}

int a1[N],a2[N];
bool vis[N];
vector<int> V;
void update(int u,int id){
    for(;u;u=t[u].fail){
        if(vis[u]) break;
        vis[u]=1;
        V.push_back(u);
        for(int i=0;i<t[u].s.size();i++)
            a1[t[u].s[i]]++,a2[id]++;
    }
}
void runAC(int id){
    vector<int> &s=a[id];
    int u=0,n=s.size();
    for(int i=0;i<n;i++){
        while(u&&!t[u].ch.count(s[i])) u=t[u].fail;
        if(t[u].ch.count(s[i])) u=t[u].ch[s[i]];
        update(u,id);
    }
    for(int i=0;i<V.size();i++) vis[V[i]]=0;
    V.clear();
}
void solve(){
    getAC();
    for(int i=1;i<=n;i++) runAC(i); 
    for(int i=1;i<=m;i++) printf("%d\n",a1[i]);
    for(int i=1;i<=n;i++) {printf("%d",a2[i]);if(i!=n) putchar(' ');}
}
int main(){
    freopen("in","r",stdin);
    n=read();m=read();
    for(int i=1;i<=n;i++){
        k=read();while(k--) a[i].push_back(read());
        a[i].push_back(-1);
        k=read();while(k--) a[i].push_back(read());
    }
    for(int i=1;i<=m;i++) ins(i);
    solve();
}
View Code

 

 

 

 

 

 

posted @ 2017-02-06 19:34  Candy?  阅读(287)  评论(0编辑  收藏  举报