【codevs1946】【BZOJ2434】阿狸的打字机,AC自动机练习

传送门1
传送门2
写在前面:15行的树状数组和35行的AC自动机和40行主程,你会先检查哪个?
思路:纠结了好久的自动机题目,不论网上的神犇们怎么说,蒟蒻就是觉得好难啊(/ □ ),算是学了一门新姿势了吧——fail树+dfs序树状数组维护AC自动机上的信息,纠结了许久,磨出来以后又和黄学长std拍了好久,发现操作几乎改的一模一样但答案还是不对,自动机和dfs改了又改,差点就推翻重写了,最后发现自己的get函数没有返回值……加上return以后又改回原程序,终于过了样例(但是T了5组?_?),之后像在hdu上做题一样把字符串长度赋给一个变量,就A了= =
网上貌似有很多详细(xuan xue)的讲解,蒟蒻还说么……推荐黄学长和Timemachine
(感觉自动机还是理解不够深刻,但时间好紧迫还有好多事没做啊啊啊)
注意:建fail树是单向边,父亲->儿子
代码:

#include<bits/stdc++.h>
#define low(x) (x&(-x))
using namespace std;
int m,x,y,root=1,tot=1,cnt,id,sum;//cnt记录自动机上的编号,id是建fail树时的边数
int d[200010],ans[100010],fail[100010],fa[100010],num[100010],trie[100010][26],first[100010],L[100010],R[100010];//L,R分别代表节点的进入,出去的顺序号
char s[200010];
queue<int>team;
struct edge
{
    int u,v,next;
}e[100010];
struct node
{
    int x,y,ind;
    bool operator <(const node other)const
    {
        return y<other.y;
    }
}q[100010];
void add(int x,int y)
{
    e[++id].u=x;
    e[id].v=y;
    e[id].next=first[x];
    first[x]=id;
}
int get(int x)
{
    int sum=0;
    while (x){
        sum+=d[x];
        x-=low(x);
    }
    return sum;
}
void update(int x,int y)
{
    while (x<=sum){
        d[x]+=y;
        x+=low(x);
    }
}
void insert(char s[])
{
    int len=strlen(s),now=root,pos=0;
    for (int i=0;i<len;i++)
    {
        if (s[i]=='B') now=fa[now];
        else if (s[i]=='P') num[++cnt]=now;
        else
        {
            if (!trie[now][s[i]-'a'])
                trie[now][s[i]-'a']=++tot,
                fa[tot]=now;
            now=trie[now][s[i]-'a'];
        }
    }
}
void build()
{
    int now,tmp;
    team.push(root);
    while (!team.empty())
    {
        now=team.front();
        team.pop();
        for (int i=0;i<26;i++)
        if (trie[now][i])
        {
            tmp=fail[now];
            while (tmp&&!trie[tmp][i]) tmp=fail[tmp];
            if (tmp&&now!=root) fail[trie[now][i]]=trie[tmp][i];
            else fail[trie[now][i]]=root;
            team.push(trie[now][i]);
        }
    }
}
void dfs(int x)
{
    L[x]=++sum;
    for (int i=first[x];i;i=e[i].next)
    dfs(e[i].v);
    R[x]=sum;
}
main()
{
    scanf("%s",s);
    insert(s);
    build();
    for (int i=1;i<=tot;i++) add(fail[i],i); 
    dfs(root);
    scanf("%d",&m);
    for (int i=1;i<=m;i++)
    {
        scanf("%d%d",&q[i].x,&q[i].y);
        q[i].ind=i;
    }
    sort(q+1,q+m+1);
    int k=0,now=root,t=1,len=strlen(s);
    for (int i=0;i<len;i++)
    {
        if (s[i]=='P')
        {
            k++;
            if (k==q[t].y)
            {
                while (q[t].y==k)
                {
                    ans[q[t].ind]=get(R[num[q[t].x]])-get(L[num[q[t].x]]-1);
                    t++;
                    if (t>m) break;
                }
            }
        }
        else if (s[i]=='B') update(L[now],-1),now=fa[now];
        else now=trie[now][s[i]-'a'],update(L[now],1);
    }
    for (int i=1;i<=m;i++)printf("%d\n",ans[i]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值