BZOJ 3172: [Tjoi2013]单词 AC自动机

3172: [Tjoi2013]单词

Description

某人读论文,一篇论文是由许多单词组成。但他发现一个单词会在论文中出现很多次,现在想知道每个单词分别在论文中出现多少次。

Input

第一个一个整数N,表示有多少个单词,接下来N行每行一个单词。每个单词由小写字母组成,N<=200,单词长度不超过10^6

Output

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

Sample Input

3
a
aa
aaa

Sample Output

6
3
1

HINT

入门

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N=1e6+10,mod=1e9+7,inf=2e9+10;

int nex[N][27],head,tail,pos[N],cnt = 1,fail[N],sum[N],q[N],n;
char a[N];
void insert(char *s,int &x) {
    int now = 1, len = strlen(s);
    for(int i = 0; i < len; ++i) {
        int index = s[i] - ‘a‘;
        if(!nex[now][index]) nex[now][index] = ++cnt;
        now = nex[now][index];
        sum[now]++;
    }
    x = now;
}
void build_fail() {
    head = 0,tail = 0;
    q[tail++] = 1;
    fail[1] = 0;
    while(head != tail) {
        int now = q[head++];
        for(int i = 0; i < 26; ++i) {
            if(!nex[now][i]) continue;
            int p = fail[now];
            while(!nex[p][i]) p = fail[p];
            fail[nex[now][i]] = nex[p][i];
            q[tail++] = nex[now][i];
        }
    }
}
int main() {
    for(int i = 0; i < 26; ++i) nex[0][i] = 1;
    scanf("%d",&n);
    for(int i = 1; i <= n; ++i) {
        scanf("%s",a);
        insert(a,pos[i]);
    }//cout<<1<<endl;
    build_fail();
    for(int i = tail-1; i >=0; --i) sum[fail[q[i]]] += sum[q[i]];
    for(int i = 1; i <= n; ++i) cout<<sum[pos[i]]<<endl;
    return 0;
}
时间: 2024-10-16 05:04:37

BZOJ 3172: [Tjoi2013]单词 AC自动机的相关文章

BZOJ 3172: [Tjoi2013]单词 [AC自动机 Fail树]

3172: [Tjoi2013]单词 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 3198  Solved: 1532[Submit][Status][Discuss] Description 某人读论文,一篇论文是由许多单词组成.但他发现一个单词会在论文中出现很多次,现在想知道每个单词分别在论文中出现多少次. Input 第一个一个整数N,表示有多少个单词,接下来N行每行一个单词.每个单词由小写字母组成,N<=200,单词长度不超过10^6

BZOJ 3172: [Tjoi2013]单词 &&  BZOJ 2434 [Noi2011]阿狸的打字机 (施工中)

fail树 链接地址 定义 把所有fail指针逆向,这样就得到了一棵树 (因为每个节点的出度都为1,所以逆向后每个节点入度为1,所以得到的是一棵树) 还账… 有了这个东西,我们可以做很多事… 对于AC自动机的构造前面的文章已经讲了,而在查询的时候,有一点感觉没有说清楚: 对于x串在y串中出现,必然是在y串某个前缀的后缀与x串相同 fail指针指向与该节点表示串后缀相等的且长度最大的串(或前缀)的节点 然后,根据fail指针的原理,在查询的时候,沿着当前节点的fail指针向上查找,直到root结束

bzoj 3172 后缀数组|AC自动机

后缀数组或者AC自动机都可以,模板题. /************************************************************** Problem: 3172 User: BLADEVIL Language: C++ Result: Accepted Time:424 ms Memory:34260 kb ****************************************************************/ //By BLADEVI

bzoj 3172 [Tjoi2013]单词(fail树,DP)

3172: [Tjoi2013]单词 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 2327  Solved: 1093[Submit][Status][Discuss] Description 某人读论文,一篇论文是由许多单词组成.但他发现一个单词会在论文中出现很多次,现在想知道每个单词分别在论文中出现多少次. Input 第一个一个整数N,表示有多少个单词,接下来N行每行一个单词.每个单词由小写字母组成,N<=200,单词长度不超过10^6

[BZOJ 3172] [Tjoi2013] 单词 【AC自动机】

题目链接:BZOJ - 3172 题目分析: 题目要求求出每个单词出现的次数,如果把每个单词都在AC自动机里直接跑一遍,复杂度会很高. 这里使用AC自动机的“副产品”——Fail树,Fail树的一个性质是,一个字符串出现的次数,就等于以它的结点为根的Fail树中的子树中所有结点的 Cnt 和. 所以把每个单词插入的时候每个字符都 ++Cnt ,在建 Fail 的时候将结点依次压入一个栈,最后再从栈顶开始弹栈,更新栈顶元素的 Fail 的 Cnt 值,这样就是自叶子节点向上更新了. 我开始写的时候

bzoj 3172: [Tjoi2013]单词

1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #define M 1000008 5 using namespace std; 6 char ch[M]; 7 int a[M][27],n,cnt=1,fa[M],f1[M],ans[M],q[M]; 8 long long sum[M],aa; 9 void jia() 10 { 11 int now=1; 12 for(int i=1;i&

BZOJ 3172 Tjoi2013 单词 fail树

题目大意及后缀数组做法见 http://blog.csdn.net/popoqqq/article/details/41042473 原来正解是fail树--难怪后缀数组被卡成这样 首先我们将给出的n个串构建AC自动机 朴素的做法是对于每个串将这个串每个节点沿着fail指针扫一遍,将路径上的所有点的cnt++ 但是这样做会TLE 我们不妨反向思考 fail指针反向后是一棵树 沿着fail指针扫一遍就是沿着树边向根扫一遍 只在插入时将每个串的每个节点cnt++ 那么每个串终点所在fail树的子树中

BZOJ 3172 Tjoi2013 单词 后缀数组

题目大意:给定一个n个单词的文章,求每个单词在文章中的出现次数 文章长度<=10^6(不是单词长度<=10^6,不然读入直接超时) 首先将所有单词用空格连接成一个字符串,记录每个单词的起始位置和长度 然后求后缀数组,对于每个单词后缀数组中一定有连续一段后缀以这个单词开头,我们通过一开始记录的起始位置找到这个单词的后缀,然后左右端点二分答案,满足左右端点之间的后缀与原单词的LCP都当与等于原单词长度即可 时间复杂度O(nlogn) #include<cstdio> #include&

[bzoj3172][Tjoi2013]单词——AC自动机

题目大意: 某人读论文,一篇论文是由许多单词组成.但他发现一个单词会在论文中出现很多次,现在想知道每个单词分别在论文中出现多少次. 思路: 第i个单词在整个文章中出现了多少次即i串的结尾可以被多少个串的节点给跳到. 于是吧fail看成每个节点唯一的父亲,每个节点的权值为有多少个单词的前缀经过了它,然后直接统计子树内的权值和即可. #include<bits/stdc++.h> #define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++