bzoj 2754 [SCOI2012]喵星球上的点名 (AC自动机+map维护Trie树)

本文介绍了一种使用AC自动机优化Trie树的方法,解决了字符集较大时的内存问题。通过实例讲解了如何根据问题构建AC自动机,并讨论了在特定条件下如何避免重复计数和正确处理未映射元素的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意:略

由于字符集大,要用map维护Trie树

并不能用AC自动机的Trie图优化,不然内存会炸

所以我用AC自动机暴跳fail水过的

显然根据喵星人建AC自动机是不行的,所以要根据问题建

然而这题有一些很艮的地方:

1.如果一个喵的名和姓都被点到,那他只被点到了一次

2.询问的串可能相同

3.如果map中并不包含某个元素,但你强行用数组表示它,那么它会返回0,然后这个元素会被强行插入map并赋值成0

#include <map>
#include <queue>
#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long 
#define ui unsigned int
#define inf 0x3f3f3f3f
#define N 100010
#define imap map<int,int>::iterator
using namespace std;
//re
int n,m;
int a[N],len[N],l[N],r[N],qans[N],use[N],b[N];
int gint()
{
    int rett=0,fh=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')fh=-1;c=getchar();}
    while(c>='0'&&c<='9'){rett=(rett<<3)+(rett<<1)+c-'0';c=getchar();}
    return rett*fh;
}
struct Trie
{
    map<int,int>ch[N];
    vector<int>ed[N];
    int val[N],fail[N],ans[N],tot;
    void build_trie(int j)
    {
        int x=0;
        for(int i=1;i<=len[j];i++){
            imap k=ch[x].find(a[i]);
            if(k==ch[x].end())
                ch[x][a[i]]=++tot,val[tot]=a[i],x=tot;
            else x=(*k).second;
            if(i==len[j]) ed[x].push_back(j);
        }
    }
    void Build()
    {
        for(int i=1;i<=m;i++){
            len[i]=gint();
            for(int j=1;j<=len[i];j++)
                a[j]=gint();
            build_trie(i);
        }
    }
    void Fail()
    {
        queue<int>q;
        int x=0,i,j,y,z;
        for(imap k=ch[0].begin();k!=ch[0].end();k++)
            q.push((*k).second);
        while(!q.empty())
        {
            x=q.front();q.pop();
            for(imap k=ch[x].begin();k!=ch[x].end();k++)
            {
                i=(*k).first;
                j=(*k).second;
                y=fail[x];
                while(!ch[y][i]&&y) 
                    y=fail[y];
                fail[j]=ch[y][i];
                q.push(j);
            }
        }
    }
    void query(int p)
    {
        int x=0;
        queue<int>q;
        for(int i=l[p];i<=r[p];i++)
        {
            while(!ch[x][b[i]]&&x)
                x=fail[x];
            x=ch[x][b[i]];
            for(int j=x;j;j=fail[j])
                if(ed[j].size()>0&&!use[j]){ 
                    for(int k=0;k<ed[j].size();k++)
                        ans[ed[j][k]]++,qans[p]++;
                    use[j]=1,q.push(j);
                }
        }
        x=0;
        for(int i=l[p+1];i<=r[p+1];i++)
        {
            while(!ch[x][b[i]]&&x)
                x=fail[x];
            x=ch[x][b[i]];
            for(int j=x;j;j=fail[j])
                if(ed[j].size()>0&&!use[j]){ 
                    for(int k=0;k<ed[j].size();k++)
                        ans[ed[j][k]]++,qans[p]++;
                    use[j]=1,q.push(j);
                }
        }
        while(!q.empty())
            {int x=q.front();q.pop();use[x]=0;}
    }
}t;

int main()
{
    //freopen("name1.in","r",stdin);
    scanf("%d%d",&n,&m);
    int cnt=1;
    for(int i=1;i<=n*2;i++){
        l[i]=cnt;
        int num=gint();
        r[i]=l[i]+num-1;
        for(int j=l[i];j<=r[i];j++)
            b[j]=gint();
        cnt=r[i]+1;
    }
    t.Build();
    t.Fail();
    for(int i=1;i<=n*2;i+=2)
        t.query(i);
    for(int i=1;i<=m;i++)
        printf("%d\n",t.ans[i]);
    for(int i=1;i<=n*2;i+=2)
        printf("%d ",qans[i]);
    puts("");
    return 0;
}





 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值