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

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

Description

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

Input

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

Output

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

Sample Input

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

Sample Output

2
1
0
1 2
【提示】
事实上样例给出的数据如果翻译成地球上的语言可以这样来看
2 3
izayoi sakuya
orihara izaya
izay
hara
raiz

HINT

【数据范围】 
 对于30%的数据,保证: 
1<=N,M<=1000,喵星人的名字总长不超过4000,点名串的总长不超过2000。
对于100%的数据,保证:
1<=N<=20000,1<=M<=50000,喵星人的名字总长和点名串的总长分别不超过100000,保证喵星人的字符串中作为字符存在的数不超过10000。


题解Here!
先来一发$AC$自动机的暴力,复杂度?$O(\text{能过})$。。。

我当然是开心的写了$AC$自动机 + 暴力转移。。。

每次沿着$fail$指针跳,然后计算, 注意判断是否计算过。

$BUT$!要用$map$存$Trie$树的儿子节点!不然?你说呢。。。

其实我也不想用$STL$,但是出题人太毒了我也没有辨法。。。

附代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<queue>
#include<map>
#include<vector>
#define MAXN 100010
using namespace std;
int n,m;
int ans_one[MAXN],ans_two[MAXN];
vector<int> last_name[MAXN],first_name[MAXN];
inline int read(){
	int date=0,w=1;char c=0;
	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
	return date*w;
}
namespace AC{
    int size=0;
    bool vis[MAXN],mark[MAXN];
    vector<int> V,M;
    struct AC_Automaton{
        int fail;
        vector<int> val;
        map<int,int> son;
        AC_Automaton(){fail=0;}
    }a[MAXN];
    void insert(int v){
        int u=0,l=read();
        for(int i=1;i<=l;i++){
            int name=read();
            if(!a[u].son[name])a[u].son[name]=++size;
            u=a[u].son[name];
        }
        a[u].val.push_back(v);
    }
    void buildtree(){
        int u,v;
        queue<int> q;
        for(map<int,int>::iterator i=a[0].son.begin();i!=a[0].son.end();i++){
            a[i->second].fail=0;
            q.push(i->second);
        }
        while(!q.empty()){
            u=q.front();
            q.pop();
            for(map<int,int>::iterator i=a[u].son.begin();i!=a[u].son.end();i++){
                int k=a[u].fail,now=i->first;
                while(k&&!a[k].son[now])k=a[k].fail;
                a[i->second].fail=a[k].son[now];
                q.push(i->second);
            }
        }
    }
    void calculate(int x,int u){
        for(int i=u;i;i=a[i].fail){
            if(!vis[i]){
                vis[i]=true;
                V.push_back(i);
                for(int j=0;j<a[i].val.size();j++)
                if(!mark[a[i].val[j]]){
                    mark[a[i].val[j]]=true;
                    M.push_back(a[i].val[j]);
                    ans_one[a[i].val[j]]++;
                    ans_two[x]++;
                }
            }
            else return;
        }
    }
    void solve(int x){
        int u=0;
        for(int i=0;i<last_name[x].size();i++){
            int c=last_name[x][i];
            while(u&&!a[u].son[c])u=a[u].fail;
            u=a[u].son[c];
            calculate(x,u);
        }
        u=0;
        for(int i=0;i<first_name[x].size();i++){
            int c=first_name[x][i];
            while(u&&!a[u].son[c])u=a[u].fail;
            u=a[u].son[c];
            calculate(x,u);
        }
        for(int i=0;i<V.size();i++)vis[V[i]]=false;
        for(int i=0;i<M.size();i++)mark[M[i]]=false;
        V.clear();M.clear();
    }
}
void work(){
    for(int i=1;i<=n;i++)AC::solve(i);
    for(int i=1;i<=m;i++)printf("%d\n",ans_one[i]);
    for(int i=1;i<=n;i++)printf("%d ",ans_two[i]);
    printf("\n");
}
void init(){
    int x,y;
    n=read();m=read();
    for(int i=1;i<=n;i++){
        x=read();
        for(int j=1;j<=x;j++){
            y=read();
            last_name[i].push_back(y);
        }
        x=read();
        for(int j=1;j<=x;j++){
            y=read();
            first_name[i].push_back(y);
        }
    }
    for(int i=1;i<=m;i++)AC::insert(i);
    AC::buildtree();
}
int main(){
    init();
    work();
    return 0;
}

然后是正解。

(坑,未填。。。)

猜你喜欢

转载自www.cnblogs.com/Yangrui-Blog/p/9431760.html