【bzoj2754】[SCOI2012]喵星球上的点名 AC自动机优化的暴力

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。

Source


数据范围吓哭了QAQ字符集1wQAQ

hzwer:map!

这题的AC自动机和暴力差不多了…这种数据范围下…

因为询问点名的串是名字的子串的数量,所以可以把点名的串建个AC自动机,然后拿名字在上面跑即可…

需要判重,一个名字不能在一次点名中点好多次,因为memset超时(…),加个栈维护清零…

一个小技巧是把姓和名用非法字符连起来,这样就能一次性处理了

然而因为太暴力了,很容易卡…不过竟然6s就A掉了

纪念第一次用迭代器,纪念第一次用这么多STL

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<map>
#include<queue>
#include<vector>
using namespace std;

const int SZ = 1000010;

map<int,int> ch[SZ];
vector<int> name[SZ],id[SZ];

int sz = 0,t[SZ],ans[SZ];

void insert(int s[],int l,int now)
{
    int p = 0;
    for(int i = 0;i < l;i ++)
    {
        int c = s[i];
        if(!ch[p][c]) ch[p][c] = ++ sz;
        p = ch[p][c];
    }
    id[p].push_back(now);
}

queue<int> q;

int fail[SZ];

void build_ac()
{
    fail[0] = 0;
    for(map<int,int> :: iterator i = ch[0].begin();i != ch[0].end();i ++)
    {
        int u = i -> second;
        q.push(u); fail[u] = 0;
    }
    while(q.size())
    {
        int f = q.front(); q.pop();
        for(map<int,int> :: iterator i = ch[f].begin();i != ch[f].end();i ++)
        {
            int c = i -> first,u = i -> second;
            q.push(u);
            int v = fail[f];
            while(v && !ch[v][c]) v = fail[v];
            fail[u] = ch[v][c];
        }
    }
}

bool vis[SZ];
vector<int> h;

int add_ans(int p)
{
    int ans = 0;
    for(;p;p = fail[p])
    {
        if(vis[p]) break;
        vis[p] = 1; h.push_back(p);
        for(int i = 0;i < id[p].size();i ++)
        {
            ans ++;
            t[id[p][i]] ++; 
        }
    }
    return ans;
}

int find(vector<int> g) //匹配到几次 
{
    int ans = 0;
    int p = 0,l = g.size();
    for(int i = 0;i < l;i ++)
    {
        int c = g[i];
        while(p && !ch[p][c]) p = fail[p];
        p = ch[p][c];
        ans += add_ans(p);
    }
    for(int i = 0;i < h.size();i ++) vis[h[i]] = 0;
    return ans;
}

int s[SZ];

int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i = 1;i <= n;i ++)
    {
        int k;
        scanf("%d",&k);
        while(k --) { int x;scanf("%d",&x); name[i].push_back(x); }
        name[i].push_back(12450);
        scanf("%d",&k);
        while(k --) { int x;scanf("%d",&x); name[i].push_back(x); }
    }
    for(int i = 1;i <= m;i ++)
    {
        int l;
        scanf("%d",&l);
        for(int j = 0;j < l;j ++)
            scanf("%d",&s[j]);
        insert(s,l,i);
    }
    build_ac();

    for(int i = 1;i <= n;i ++)
        ans[i] = find(name[i]);

    for(int i = 1;i <= m;i ++)
        printf("%d\n",t[i]); //第i个串被匹配多少次 

    for(int i = 1;i <= n;i ++)
        printf("%d%c",ans[i],i == n ? '\n' : ' ');
    return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值