[bzoj2434][Noi2011]阿狸的打字机——AC自动机

本文介绍了一种使用AC自动机解决字符串匹配问题的方法,通过构建Trie树和失败指针,实现了高效的字符串匹配查询。文章详细解释了算法思路,并提供了完整的代码实现。

题目大意:

给定n个字符串,每次询问第x个字符串在第y个字符串中出现了多少次。

思路:

显然我们需要先把AC自动机给建出来。
考虑如何最暴力地计算第x个字符在第y个字符中出现了多少次,我们可以在Trie上暴力跳y的每一个节点,然后对于y的每一个节点跳fail,如果跳到了x串的结尾,那么答案+1。
这样对于每一个节点都跳fail显然复杂度无法接受,那么换一个角度考虑,x的结尾可以被y串上的多少个节点给跳到。
于是我们可以把每个节点的fail看成是它的父亲,然后问题就转化为了求x的结尾点的子树中包含了多少个y的节点,这样以后可以暴力标记y上的每一个节点,对于x直接子树求和即可。
这样以后,对于y相同的二元组就可以同时处理了,相当于把x挂在了y上。
但是这样复杂度还是太高,无法接受,于是我们发现复杂度主要在暴力标记y上的每一个节点上,于是考虑将询问按照y离线,把y挂在串y的结尾点上,然后按照dfs的顺序遍历Trie树上的每一个节点,进入这个节点时打上标记,回溯时将标记撤回,这样到了y的结尾的时候,不难发现此时打上标记的点恰好是y串上所有的点。

#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define debug(x) cout<<#x<<"="<<x<<" "
#define pii pair<int,int>
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;

using namespace std;

void File(){
    freopen("bzoj2434.in","r",stdin);
    freopen("bzoj2434.out","w",stdout);
}

template<typename T>void read(T &_){
    _=0; T f=1; char c=getchar();
    for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
    for(;isdigit(c);c=getchar())_=(_<<1)+(_<<3)+(c^'0');
    _*=f;
}

const int maxn=1e5+10;
int m;

int ch[maxn][26],num[maxn],fail[maxn],cnt;
int fa[maxn],le[maxn],pos[maxn];
vector<int>rank[maxn];

void print(int u){
    if(fa[u]!=1)print(fa[u]);
    putchar(le[u]+'a');
}

void init(){
    fail[cnt=1]=1;

    char s[maxn];
    scanf("%s",s+1);

    int len=strlen(s+1),u=1,c,cnt_rank=0;
    REP(i,1,len){
        if(s[i]=='B')u=fa[u];
        else if(s[i]=='P'){
            ++num[u];
            rank[u].pb(++cnt_rank);
            pos[cnt_rank]=u;
        }
        else{
            c=s[i]-'a';
            if(!ch[u][c])ch[u][c]=++cnt;
            fa[ch[u][c]]=u,le[ch[u][c]]=c;
            u=ch[u][c];
        }
    }
}

void build_fail(){ int h=1,t=0,q[maxn];
    REP(i,0,25)if(ch[1][i]){
        fail[ch[1][i]]=1;
        q[++t]=ch[1][i];
    }

    while(h<=t){
        int u=q[h++];
        REP(i,0,25)if(ch[u][i]){
            int v=ch[u][i],p=fail[u];
            q[++t]=v;
            while(p!=1 && !ch[p][i])p=fail[p];
            if(ch[p][i])fail[v]=ch[p][i];
            else fail[v]=1;
        }
    }
}

int beg[maxn],to[maxn],las[maxn],cnte,dfn[maxn],cnt_dfn,sz[maxn];

void add(int u,int v){
    las[++cnte]=beg[u]; beg[u]=cnte; to[cnte]=v;
}

void dfs_dfn(int u){
    dfn[u]=++cnt_dfn;
    sz[u]=1;
    for(int i=beg[u];i;i=las[i]){
        dfs_dfn(to[i]);
        sz[u]+=sz[to[i]];
    }
}

struct BIT{
    int sum[maxn];
    int lowbit(int x){
        return x&(-x);
    }
    void modify(int p,int x){
        for(;p<=cnt;p+=lowbit(p))sum[p]+=x;
    }
    int query(int p){
        int ret=0;
        for(;p>=1;p-=lowbit(p))ret+=sum[p];
        return ret;
    }
}T;

int ans[maxn];
vector<pii>qu[maxn];

void solve(int u){
    T.modify(dfn[u],1);

    REP(i,0,rank[u].size()-1){
        int y=rank[u][i];
        REP(j,0,qu[y].size()-1){
            int x=pos[qu[y][j].fi];
            ans[qu[y][j].se]=T.query(dfn[x]+sz[x]-1)-T.query(dfn[x]-1);
        }
    }

    REP(i,0,25)if(ch[u][i]){
        int v=ch[u][i];
        solve(v);
    }
    T.modify(dfn[u],-1);
}

int main(){
    File();

    init();

    build_fail();

    REP(i,2,cnt)add(fail[i],i);
    dfs_dfn(1);

    int x,y;
    read(m);
    REP(i,1,m){
        read(x),read(y);
        qu[y].pb(mk(x,i));
    }

    solve(1);
    REP(i,1,m)printf("%d\n",ans[i]);

    return 0;
}

转载于:https://www.cnblogs.com/ylsoi/p/10182087.html

欢迎使用“可调增益放大器 Multisim”设计资源包!本资源专为电子爱好者、学生以及工程师设计,旨在展示如何在著名的电路仿真软件Multisim环境下,实现一个具有创新性的数字控制增益放大器项目。 项目概述 在这个项目中,我们通过巧妙结合模拟电路与数字逻辑,设计出一款独特且实用的放大器。该放大器的特点在于其增益可以被精确调控,并非固定不变。用户可以通过控制键,轻松地改变放大器的增益状态,使其在1到8倍之间平滑切换。每一步增益的变化都直观地通过LED数码管显示出来,为观察和调试提供了极大的便利。 技术特点 数字控制: 使用数字输入来调整模拟放大器的增益,展示了数字信号对模拟电路控制的应用。 动态增益调整: 放大器支持8级增益调节(1x至8x),满足不同应用场景的需求。 可视化的增益指示: 利用LED数码管实时显示当前的放大倍数,增强项目的交互性和实用性。 Multisim仿真环境: 所有设计均在Multisim中完成,确保了设计的仿真准确性和学习的便捷性。 使用指南 软件准备: 确保您的计算机上已安装最新版本的Multisim软件。 打开项目: 导入提供的Multisim项目文件,开始查看或修改设计。 仿真体验: 在仿真模式下测试放大器的功能,观察增益变化及LED显示是否符合预期。 实验与调整: 根据需要调整电路参数以优化性能。 实物搭建 (选做): 参考设计图,在真实硬件上复现实验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值