洛谷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\n", ac.cnt[pos[i]]);
    }

    return 0;
}
全部评论

相关推荐

大方的大熊猫准备进厂:1.教育背景:你希望从事什么专业的工作你的主修课就是什么;成绩优秀是你应该做的,没什么可描述的,成绩不优秀也许人家在大学忙着创业呢?(成绩优秀不一定是好事,只能说明多元化的大学你上成了高中,没有真正上明白大学,反而体现了你死板,不爱社交,没有别的突出能力) 2.实践经历:你想表达的意思没有说清楚。你是说你会个性化服务,还是你有实习经历。如果没有带来,经济收益,表彰,更好的发展前景,那你还不如说说提升了自己哪些技能。你说有人给你送锦旗我都能明白你优秀,但是你说你会xxxx,你说这话谁信,证据呢。 3.入伍经历:你描述的就是你的工作职责或者你应该做的,并没有体现出来你把这个事情做好了,而且入伍经历并不能证明你能干好你要应聘的工作,不如只写经历其余所有内容都不写。 4.荣誉技能:重点突出一下,但不要过多描述,这些荣誉的含金量懂得都懂。 重点:你要应聘什么工作(具体岗位,实习生不具体),你的期望薪资
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务