• CF1207G Indie Album


    CF1207G Indie Album

    一道很有意思的AC自动机好题。

    AC自动机的本质是记录了一棵字典树上所有字符串的所有后缀,所以这道题我们可以充分利用这个性质。

    这道题与板子题的不同之处就在于:文本串在不停的变化,每次增加一个字符。

    我们先来复习一下AC自动机模版题的流程

    1. 遍历文本串的所有前缀。
    2. 将所有是这个前缀的后缀的模式串出现次数+1。(即沿着fail指针一直走到根,沿途的所有节点都是符合条件的)

    而对于这道题,因为文本串每次只会添加一个字符。所以我们在上一个文本串的基础上,加入以当前节点到根组成的字符串的贡献即可。

    那这道题的解决方案就比较清晰了:

    我们将字符串(s)定义为当前字典树节点到根的所有节点组成的字符串,文本串(s)对应的模式串出现次数的记为(t[s]),将文本串(s)对应的答案记为(ans[s])

    1. 建出一个文本串组成的字典树。
    2. 对字典树进行DFS,每走到一个节点,将所有为s后缀的字符串的出现次数(t)加1。
    3. 记录答案(ans[s]=t[s])
    4. DFS回溯时,将所有为s后缀的字符串的出现次数(t)减1。

    因为所有字符串长度和(T)的大小关系,(T^2)算法是过不了的。但是因为累计答案是在(fail)指针构成的树,所以我们可以用数据结构来维护这个东西。复杂度变成了(Tlog T)

    个人感觉这道题的关键在于利用AC自动机“求解单文本串问题时会遍历文本串的所有前缀”这个性质,从而求解特殊的多文本串问题。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<string>
    #include<vector>
    #define ll long long
    #define maxn (int)(4*1e5+1000)
    using namespace std;
    int ans[maxn],ch[maxn][30],tr[maxn][30],c[maxn],queue[maxn],n,m,now_insert,tcnt,acnt,fcnt,ver_tr[maxn],fail[maxn],size[maxn],id[maxn];//id指在ac自动机上的点在fail树上的编号
    bool is_end[maxn];
    char s[10];
    struct gg{
        int lo,id,mod_ed;string add;//版本和询问的模式串,以及模式串结尾位置在AC自动机上的编号
    }query[maxn];
    vector<int>tr_ver[maxn],fail_son[maxn];
    vector<gg>so[maxn];//记录每个版本的询问
    void change(int now,int num) {
        for(int i=now;i<=fcnt;i+=i&-i){c[i]+=num;}
    }
    int sum(int now) {
        int re=0;for(int i=now;i>=1;i-=i&-i){re+=c[i];}return re;
    }
    int insert_ac(string data) {
        int now=0;
        for(int i=0;i<data.size();i++) {
            int cha=data[i]-'a';
            if(!ch[now][cha])ch[now][cha]=++acnt;
            now=ch[now][cha];
        }
        is_end[now]=1;return now;
    }
    void build_ac() {
        int l=1,r=0;
        for(int i=0;i<30;i++)if(ch[0][i]){queue[++r]=ch[0][i];fail_son[0].push_back(ch[0][i]);}
        while(l<=r) {
            int now=queue[l];l++;
            for(int i=0;i<30;i++) {
                if(ch[now][i]) {
                    queue[++r]=ch[now][i];
                    fail[ch[now][i]]=ch[fail[now]][i];
                    fail_son[ch[fail[now]][i]].push_back(ch[now][i]);
                }
                else ch[now][i]=ch[fail[now]][i];
            }
        }
    }
    void dfs1(int now) {
        id[now]=++fcnt;size[id[now]]=1;
        for(int i=0;i<fail_son[now].size();i++){dfs1(fail_son[now][i]);size[id[now]]+=size[id[fail_son[now][i]]];}
    }
    void update(int now,int num) {//更改AC自动机上now点所有儿子
        change(id[now],num);
    }
    void get_ans(gg data,int fail_now) {
        ans[data.id]=sum(fail_now+size[fail_now]-1)-sum(fail_now-1);
    }
    void dfs2(int tr_now,int ac_now) {
        if(tr_now) {
            update(ac_now, 1);//所有匹配的模式串加上1。
            for (int i = 0; i < tr_ver[tr_now].size(); i++) {//枚举以当前点结尾的所有版本
                int now_ver = tr_ver[tr_now][i];//现在的处理版本
                for (int j = 0; j < so[now_ver].size(); j++) {
                    gg now_qu = so[now_ver][j];//现在处理的询问
                    get_ans(now_qu,id[now_qu.mod_ed]);
                }
            }
        }
        for(int i=0;i<30;i++) {
            if(tr[tr_now][i]) {
                dfs2(tr[tr_now][i],ch[ac_now][i]);
            }
        }
        if(tr_now) {
            update(ac_now,-1);
        }
    }
    int main() {
        scanf("%d",&n);
        for(int i=1;i<=n;i++) {
            int ty;scanf("%d",&ty);
            if(ty==1) {
                scanf("%s",s);
                now_insert=0;
            }
            else {
                scanf("%d",&now_insert);now_insert=ver_tr[now_insert];scanf("%s",s);
            }
            int tar=s[0]-'a',now;
            if(!tr[now_insert][tar])tr[now_insert][tar]=++tcnt;
            now=tr[now_insert][tar];
            tr_ver[now].push_back(i);
            ver_tr[i]=now;
        }
        scanf("%d",&m);
        for(int i=1;i<=m;i++) {
            scanf("%d",&query[i].lo);cin>>query[i].add;query[i].mod_ed=insert_ac(query[i].add);query[i].id=i;
            so[query[i].lo].push_back(query[i]);
        }
        build_ac();
        dfs1(0);
        dfs2(0,0);
        for(int i=1;i<=m;i++)printf("%d
    ",ans[i]);
    }
    

    另外,记录一下本题的小故事:

  • 相关阅读:
    深度学习优化方法比较
    调参
    Numpy/Pytorch之数据类型与强制转换
    numpy:维度问题
    js模板引擎-juicer
    js模板引擎-腾讯artTemplate 简洁语法例子
    canva绘制时钟
    js中的break ,continue, return
    JavaScript奇技淫巧44招
    数据类型
  • 原文地址:https://www.cnblogs.com/GavinZheng/p/11421730.html
Copyright © 2020-2023  润新知