zoukankan      html  css  js  c++  java
  • 洛谷P3966 [TJOI2013]单词 单词 (ac自动机 fail树的应用)

    洛谷P3966 [TJOI2013]单词 单词 (ac自动机 fail树的应用)

    题目链接

    概述:

    ac自动机的fail指针构建成了一颗树。如果fail[p]指向q。那么我们假设根到p的字符串为str, 根到q的字符串为ttr. 那么str的后缀就是ttr的前缀, 某字符串在自动机上出现了。
    

    对于这题, 每个在每个单词插入字典树的时候一路加加,代表这个单词的这个前缀出现过。那么整个自动机就是我们的文章。还有些情况我们需要统计就是单词作为某些后缀出现的情况。cnt[ fail[x] ] += cnt[x] 这样我们按深度由深到浅计算贡献即可(避免重复计算)。同时我们发现整个贡献计算就是队列的逆顺序。所以我存到了一个栈中。
    

    参考代码

    ps:自动机风格是看着kuangbin的学的

    #include <bits/stdc++.h>
    
    using namespace std;
    const int MAXN = 1e6 + 7;
    
    char buf[MAXN];
    
    int pos[MAXN], n;
    
    stack<int>st;
    
    struct ACauto {
    
        static const int MAXN = 1e6 + 7;
    
        int fail[MAXN], ch[MAXN][30], cnt[MAXN], tot, root;
    
        int new_node() {
            for(int i = 0; i < 26; i++ ) {
                ch[tot][i] = -1;
            }
            cnt[tot++] = 0;
            return tot - 1;
        }
    
        void init() {
            tot = 0;
            root = new_node();
        }
    
        void insert(char *str, int len, int id) {
            int now = root;
            for(int i = 0; i < len; i++ ) {
                if(ch[now][ str[i] - 'a' ] == -1) {
                    ch[now][ str[i] - 'a' ] = new_node();
                }
                now = ch[now][ str[i] - 'a' ];
                cnt[now]++;
            }
            pos[id] = now;
        }
    
        void get_fail() {
            queue<int>que;
            fail[root] = root;
            for(int i = 0; i < 26; i++ ) {
                if(ch[root][i] == -1) {
                    ch[root][i] = root;
                } else {
                    fail[ ch[root][i] ] = root;
                    que.push(ch[root][i]);
                }
            }
            while(!que.empty()) {
                int now = que.front();
                que.pop();
                st.push(now);
                for(int i = 0; i < 26; i++ ) {
                    if(ch[now][i] == -1) {
                        ch[now][i] = ch[ fail[now] ][i];
                    } else {
                        fail[ ch[now][i] ] = ch[ fail[now] ][i];
                        que.push(ch[now][i]);
                    }
                }
            }
        }
    
        int query(char str[], int len) {
            int now = root;
            int res = 0;
            for(int i = 0; i < len; i++ ) {
                now = ch[now][ str[i] - 'a' ];
                int pos = now;
                while(pos != root) {
                    res += cnt[pos];
                    cnt[pos] = 0;
                    pos = fail[pos];
                }
            }
            return res;
        }
    
    };
    
    ACauto ac;
    
    int main() {
        scanf("%d", &n);
        ac.init();
        for(int i = 1; i <= n; i++ ) {
            scanf("%s", buf);
            ac.insert(buf, strlen(buf), i);
        }
        ac.get_fail();
        while(!st.empty()) {
            ac.cnt[ ac.fail[st.top()] ] += ac.cnt[ st.top() ];
            st.pop();
        }
        for(int i = 1; i <= n; i++ ) {
            printf("%d
    ", ac.cnt[pos[i]]);
        }
    
        return 0;
    }
    
    
  • 相关阅读:
    Linux命令详解之—tail命令
    Linux命令详解之—less命令
    Linux命令详解之—more命令
    Linux命令详解之—cat命令
    Linux命令详解之—pwd命令
    Linux命令详解之–cd命令
    ubuntu-14.04安装最新tensorflow记录
    rn最新版测试
    boost asio死锁一例
    dskinlite自适应dpi
  • 原文地址:https://www.cnblogs.com/Q1143316492/p/9568829.html
Copyright © 2011-2022 走看看