zoukankan      html  css  js  c++  java
  • BZOJ3172: [Tjoi2013]单词

    BZOJ3172: [Tjoi2013]单词

    Description

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

    Input

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

    Output

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

    Sample Input

    3
    a
    aa
    aaa

    Sample Output

    6
    3
    1

    题解Here!
    本来是想找后缀数组的,然后找到了一道$AC$自动机的裸题。。。

    把所有单词扔到$AC$自动机里。 当$insert$的时候记录这条路径上的所有点$val++$。

    设$p$的$fail$指针指向$q$。

    因为根到$p$的路径的某一后缀等同于根到$q$,所以$p$节点为$q$节点贡献的答案是$val[p]$。

    具体操作就是记录$buildtree$时的队列,逆序处理为其的$fail$指针贡献答案。

    有点像拓扑排序。。。

    因为在队列里的节点的深度是单调不下降的,所以这么做是对的。

     附代码:

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #define MAXN 1000010
    #define MAXM 210
    using namespace std;
    int n,m=0,size=0;
    int id[MAXM],num[MAXN];
    char ch[MAXN];
    struct AC{
        int fail,val,son[26];
        AC(){
            fail=val=0;
            memset(son,0,sizeof(son));
        }
    }a[MAXN];
    inline int read(){
        int date=0,w=1;char c=0;
        while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
        while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
        return date*w;
    }
    inline int idx(char x){return x-'a';}
    int insert(char *s){
        int u=0,l=strlen(s);
        for(int i=0;i<l;i++){
            int c=idx(s[i]);
            if(!a[u].son[c])a[u].son[c]=++size;
            u=a[u].son[c];
            a[u].val++;
        }
        return u;
    }
    void buildtree(){
        int u,v;
        queue<int> q;
        num[++m]=0;
        for(int i=0;i<26;i++)
        if(a[0].son[i]){
            a[a[0].son[i]].fail=0;
            q.push(a[0].son[i]);
        }
        while(!q.empty()){
            u=q.front();
            q.pop();
            num[++m]=u;
            for(int i=0;i<26;i++){
                if(a[u].son[i]){
                    a[a[u].son[i]].fail=a[a[u].fail].son[i];
                    q.push(a[u].son[i]);
                }
                else a[u].son[i]=a[a[u].fail].son[i];
            }
        }
    }
    void query(){
        for(int i=m;i>=1;i--)a[a[num[i]].fail].val+=a[num[i]].val;
    }
    void work(){
        query();
        for(int i=1;i<=n;i++)printf("%d
    ",a[id[i]].val);
    }
    void init(){
        n=read();
        for(int i=1;i<=n;i++){
            scanf("%s",ch);
            id[i]=insert(ch);
        }
        buildtree();
    }
    int main(){
        init();
        work();
        return 0;
    }
    
  • 相关阅读:
    [Web] 网络安全(SSH SSL HTTPS)
    [OS] 操作系统课程(三)
    [刷题] 203 Remove Linked List Elements
    [刷题] 206 Reverse Linked List
    for循环中let与var的区别,块级作用域如何产生与迭代中变量i如何记忆上一步的猜想
    es6入门1-- let与var的区别详解
    精读JavaScript模式(八),JS类式继承与现代继承模式其一
    精读JavaScript模式(七),命名空间模式,私有成员与静态成员
    精读JavaScript模式(六),Memoization模式与函数柯里化的应用
    精读JavaScript模式(五),函数的回调、闭包与重写模式
  • 原文地址:https://www.cnblogs.com/Yangrui-Blog/p/9445406.html
Copyright © 2011-2022 走看看