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

AC自动机

对每一个询问串建AC自动机,拿每一个名字跑AC自动机,不断在fail指针上统计答案即可。因为字符串总长度有保证,时间复杂度可以近似看成O(LEN)。

刚开始比较SB,每一个trie节点都开了一个next[10000],结果MLE了。实际上每个节点根本用不到这么多,可以改成map。

做fail指针的时候不用从1扫到10000来找子节点,因为子节点并没有那么多,可以把子节点的编号用vector记录下来,要不然应该会T。

#include<cstdio>
#include<vector>
#include<queue>
#include<map>
#include<cstring>
#define N 20005
#define M 50005
#define NUM 10005
#define LEN 1000005
using namespace std;
int in()
{
    int r=0;
    char c=getchar();
    while(c<'0'||c>'9')c=getchar();
    while(c>='0'&&c<='9')r=r*10+c-'0',c=getchar();
    return r;
}
vector<int> name[N][2];
vector<int> son[LEN];
int hash[M], ans[N], nodecnt=0, cnt[LEN], siz[LEN], vis[LEN];
struct node
{
    node *fail;
    map<int,node*> next;
    int id;
    node(){id=0;fail=NULL;}
};
struct ACAutomaton
{
    node *root;
    void init()
    {
        root=new node();
        root->fail=root;
        root->id=++nodecnt;
    }
    void insert(int len, int id)
    {
        node *p=root;
        for(int k = 1; k <= len; k++)
        {
            int x=in();
            if(p->next[x]==NULL)
            {
                p->next[x]=new node();
                p->next[x]->id=++nodecnt; 
                son[p->id].push_back(x);
            }
            p=p->next[x];
        }
        hash[id]=p->id;
        siz[p->id]++;
    }
    void build()
    {
        queue<node*> q;
        for(int j = 0, jj = son[root->id].size();j < jj; j++)
        {
            int i=son[root->id][j];
            root->next[i]->fail=root;
            q.push(root->next[i]);
        }
        while(!q.empty())
        {
            node *u=q.front();
            q.pop();
            for(int j = 0, jj = son[u->id].size(); j < jj; j++)
            {
                int i=son[u->id][j];
                node *v=u->next[i], *t=u->fail;
                while(t!=root && !t->next[i])
                {
                    t=t->fail;
                }
                v->fail=t->next[i]?t->next[i]:root;
                q.push(v);
            }
        }
    }
    void solve(int x)
    {
        for(int k = 0; k < 2; k++)
        {
            node *p=root;
            for(int i = 0, ii = name[x][k].size(); i < ii; i++)
            {
                int c=name[x][k][i];
                while(p!=root && !p->next[c])p=p->fail;
                if(p->next[c])p=p->next[c];
                node *temp=p;
                for(;temp!=root;temp=temp->fail)
                {
                    if(vis[temp->id]==x)continue;
                    vis[temp->id]=x;
                    ans[x]+=siz[temp->id];
                    cnt[temp->id]++;
                }
            }
        }
    }
}AC;
int main()
{
    AC.init(); 
    int n, m;
    scanf("%d%d",&n,&m);
    for(int i = 1; i <= n; i++)
    {
        for(int j = 0; j < 2; j++)
        {
            int k;
            scanf("%d",&k);
            for(int g = 1; g <= k; g++)
                name[i][j].push_back(in());
        }
    }
    for(int i = 1; i <= m; i++)
    {
        int k=in();
        AC.insert(k,i);
    }
    AC.build();
    for(int i = 1; i <= n; i++)
        AC.solve(i);
    for(int i = 1; i <= m; i++)
        printf("%d\n",cnt[hash[i]]);
    for(int i = 1; i < n; i++)
        printf("%d ",ans[i]);
    printf("%d",ans[n]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值