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

3172: [Tjoi2013]单词

Time Limit: 10 Sec  Memory Limit: 512 MB
Submit: 3198  Solved: 1532
[Submit][Status][Discuss]

Description

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

Input

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

Output

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

Sample Input

3
a
aa
aaa

Sample Output

6
3
1


本题的意思是说文本就是那些单词组成的.......(中间会有空格吧)

如果朴素做法建一个AC自动机然后一个个单词当做文本来匹配(走,cnt++)时间复杂度受不了】

于是发现有个神奇的地方,匹配的模式同样也是文本,建AC自动机的时候已经走过一次了啊!

Trie树ins的时候经过节点就cnt++,(也就是说这个模板能匹配到当前插入的文本)

然后统计每个单词终点节点的Fail树中子树cnt和就可以了

(因为“某个结点所对应的字符串肯定是其后代结点所对应的字符串的后缀”啊)

PS:如何统计Fail子树和?队列中正好是BFS序,队列从后往前更新fail指针的cnt就可以了

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=1e6+5,M=205;
int n;
char s[N];
struct node{
    int ch[26],fail,cnt;
}t[N];
int pos[M],sz;
void ins(char s[],int &x){
    int u=0,n=strlen(s+1);
    for(int i=1;i<=n;i++){
        int c=s[i]-‘a‘;
        if(!t[u].ch[c]) t[u].ch[c]=++sz;
        u=t[u].ch[c];
        t[u].cnt++;
    }
    x=u;
}
int q[N],head,tail;
void getFail(){
    head=tail=1;
    for(int i=0;i<26;i++)
        if(t[0].ch[i]) q[tail++]=t[0].ch[i];
    while(head!=tail){
        int u=q[head++];
        for(int i=0;i<26;i++){
            int &v=t[u].ch[i];
            if(!v) {v=t[t[u].fail].ch[i];continue;}
            t[v].fail=t[t[u].fail].ch[i];
            q[tail++]=v;
        }
    }
    for(int i=tail-1;i>=1;i--) t[t[q[i]].fail].cnt+=t[q[i]].cnt;
}
int main(){
    //freopen("in.txt","r",stdin);
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%s",s+1);
        ins(s,pos[i]);
    }
    getFail();
    for(int i=1;i<=n;i++) printf("%d\n",t[pos[i]].cnt);
}
时间: 2024-10-22 10:35:56

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

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 入门 #

BZOJ 题目3172: [Tjoi2013]单词(AC自动机||AC自动机+fail树||后缀数组暴力||后缀数组+RMQ+二分等五种姿势水过)

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

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]单词 &&  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] 单词 【AC自动机】

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

BZOJ 3172 Tjoi2013 单词 fail树

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

【AC自动机/fail树】BZOJ3172- [Tjoi2013]单词

[题目大意] http://www.lydsy.com:808/JudgeOnline/problem.php?id=3172 某人读论文,一篇论文是由许多单词组成.但他发现一个单词会在论文中出现很多次,现在想知道每个单词分别在论文中出现多少次. [思路] 第一次写fail树.首先建立AC自动机,对于路径上的每一个点sum++,表示出现的次数.fail指针指向的后缀,如果从fail指针指向的点开始方向建立fail树,其子树的sum之和就等于以它作为后缀的串的总数,相当于它在文章中出现的个数. *

BZOJ 2434: [Noi2011]阿狸的打字机 [AC自动机 Fail树 树状数组 DFS序]

2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 2545  Solved: 1419[Submit][Status][Discuss] Description 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母.经阿狸研究发现,这个打字机是这样工作的:l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最