【NOI2011】bzoj2434 阿狸的打字机

本文介绍了一种使用AC自动机和Trie树解决特定问题的方法,通过将输入字符流映射为Trie树的构建过程,并利用AC自动机进行高效查询。文章详细解释了如何维护fail指针及子树查询,最终实现单点修改和区间查询。

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

可以把打字的过程看成trie树的构建。把trie树的AC自动机建出来,问题就是询问一个点到根的路径上,有多少点的fail会指到另一个点上。
因为fail指针是树的结构,可以沿着原操作在trie树上走,维护fail树上单点修改、子树查询的操作。按照dfs序用树状数组维护。

#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn=200010;
vector<int> qry[maxn],son[maxn];
char s[maxn];
int trans[maxn][30],fail[maxn],fa[maxn],que[maxn],
L[maxn],R[maxn],sum[maxn],qx[maxn],ans[maxn],
pos[maxn],
n,q,tot,num,clo;
void dfs(int u)
{
    L[u]=++clo;
    vector<int>::iterator it;
    for (it=son[u].begin();it!=son[u].end();it++) dfs(*it);
    R[u]=clo;
}
int query(int p)
{
    int ret=0;
    for (;p;p-=p&-p) ret+=sum[p];
    return ret;
}
void add(int p,int x)
{
    for (;p<=clo;p+=p&-p) sum[p]+=x;
}
int main()
{
    //freopen("in","r",stdin);
    int p=0,hd=1,tl=0,u,v;
    vector<int>::iterator it;
    scanf("%s",s+1);
    n=strlen(s+1);
    for (int i=1;i<=n;i++)
        if (s[i]=='P') pos[++num]=p;
        else if (s[i]=='B') p=fa[p];
        else
        {
            if (!trans[p][s[i]-'a']) fa[trans[p][s[i]-'a']=++tot]=p;
            p=trans[p][s[i]-'a'];
        }
    for (int i=0;i<26;i++)
        if (trans[0][i]) que[++tl]=trans[0][i];
    while (hd<=tl)
    {
        u=que[hd++];
        for (int i=0;i<26;i++)
            if (trans[u][i])
            {
                que[++tl]=trans[u][i];
                fail[trans[u][i]]=trans[fail[u]][i];
            }
            else trans[u][i]=trans[fail[u]][i];
    }
    for (int i=1;i<=tot;i++) son[fail[i]].push_back(i);
    scanf("%d",&q);
    for (int i=1;i<=q;i++)
    {
        scanf("%d%d",&u,&v);
        qx[i]=pos[u];
        qry[pos[v]].push_back(i);
    }
    dfs(0);
    p=0;
    for (int i=1;i<=n;i++)
        if (s[i]=='P')
            for (it=qry[p].begin();it!=qry[p].end();it++) ans[*it]=query(R[qx[*it]])-query(L[qx[*it]]-1);
        else if (s[i]=='B')
        {
            add(L[p],-1);
            p=fa[p];
        }
        else
        {
            p=trans[p][s[i]-'a'];
            add(L[p],1);
        }
    for (int i=1;i<=q;i++) printf("%d\n",ans[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值