zoukankan      html  css  js  c++  java
  • 【洛谷】3966:[TJOI2013]单词【AC自动机】【fail树】

    P3966 [TJOI2013]单词

    题目描述

    小张最近在忙毕设,所以一直在读论文。一篇论文是由许多单词组成但小张发现一个单词会在论文中出现很多次,他想知道每个单词分别在论文中出现了多少次。

    输入输出格式

    输入格式:

     

    第一行一个整数N,表示有N个单词。接下来N行每行一个单词,每个单词都由小写字母(a-z)组成。(N≤200)

     

    输出格式:

     

    输出N个整数,第i行的数表示第i个单词在文章中出现了多少次。

    输入输出样例

    输入样例#1: 复制
    3
    a
    aa
    aaa
    输出样例#1: 复制
    6
    3
    1
    

    说明

    数据范围

    30%的数据, 单词总长度不超过10^3

    100%的数据,单词总长度不超过10^6


    Solution

    解法也是挺简单的,首先肯定是建出ac自动机。

    然后我们考虑fail指针的含义,$fail[u]$到根节点是$u$到根节点串的后缀,也就是以$fail[u]$为结尾的串在$u$中出现了$cnt[u]$次...

    所有的fail都是这样的,所以反向建fail指针变成fail树,统计子树和更新$cnt$,$cnt[u]$就是每个$u$为结尾的串出现的次数了。

    在bfs的时候存下遍历顺序,最后倒着更新就可以了。

    Code

    #include<bits/stdc++.h>
    #define LL long long
    using namespace std;
    
    string s[1000005];
    int n;
    LL ans[1000005], cnt[1000005];
    int son[1000005][27], fail[1000005], tail, las[1000005];
    void insert(string a, int opt) {
        int len = a.length();
        int nd = 0;
        for(int i = 0; i < len; i ++) {
            int t = a[i] - 'a' + 1;
            if(!son[nd][t])    son[nd][t] = ++ tail;
            nd = son[nd][t];
            cnt[nd] ++;
        }
        las[opt] = nd;
    }
    
    int Q[1000005], t;
    void get_fail() {
        queue < int > q;
        Q[++t] = 0;
        for(int i = 1; i <= 26; i ++)    if(son[0][i])    fail[son[0][i]] = 0, q.push(son[0][i]);
        while(!q.empty()) {
            int u = q.front(); q.pop();
            for(int i = 1; i <= 26; i ++) {
                if(son[u][i])    fail[son[u][i]] = son[fail[u]][i], q.push(son[u][i]), Q[++t] = son[u][i];
                else            son[u][i] = son[fail[u]][i];
            }
        }
    }
    
    int main() {
        scanf("%d", &n);
        for(int i = 1; i <= n; i ++) {
            cin >> s[i];
            insert(s[i], i);
        }
        get_fail();
        for(int i = t; i; i --) {
            int now = Q[i];
            cnt[fail[now]] += cnt[now];
        }
        for(int i = 1; i <= n; i ++)
            printf("%lld
    ", cnt[las[i]]);
        return 0;
    }
  • 相关阅读:
    沙县小吃炖罐做法 114沙县小吃配料网
    党参_百度百科
    EF架构~通过EF6的DbCommand拦截器来实现数据库读写分离~续~添加事务机制
    EF架构~通过EF6的DbCommand拦截器来实现数据库读写分离
    知方可补不足~Sqlserver中的几把锁和.net中的事务级别
    面对大数据,我们应该干的事~大话开篇
    EF架构~在T4模版中自定义属性的getter和setter
    SurfaceView的一个小应用:开发示波器
    Jetty入门
    ios ARC
  • 原文地址:https://www.cnblogs.com/wans-caesar-02111007/p/9806602.html
Copyright © 2011-2022 走看看