洛谷P3966 [TJOI2013]单词 [AC自动机]

  题目传送门

单词

题目描述

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

输入输出格式

输入格式:

 

第一行一个整数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


  分析:

  此题有多种做法,hash(太麻烦不想打),后缀数组(不会),AC自动机(就是你了)。为了复习一波AC自动机,蒟蒻选择了此种做法。

  实际上也算是比较明显的AC自动机了,在做的时候记录路径就可以轻松过了,不过蒟蒻本来也不擅长字符串算法,所以更加详细的解释还是参考别的大佬们的博客吧,蒟蒻就只放代码了。

  Code:

//It is made by HolseLee on 10th Aug 2018
//Luogu.org P3966
#include<bits/stdc++.h>
using namespace std;

const int N=202;
const int L=1e6+7;
int n,ans[N],t[L][26],tot,all,val[L],fail[L],lis[L],mk[L];
char s[L+N],a[L];
queue<int>T;

struct Trie{
    void ins(int num)
    {
        int len=strlen(a),root=0,v;
        for(int i=0;i<len;++i){
            s[all++]=a[i];
        }
        s[all++]='#';
        for(int i=0;i<len;++i){
            v=a[i]-'a';
            if(!t[root][v])t[root][v]=++tot;
            root=t[root][v];
        }
        if(!val[root])val[root]=num;
        mk[num]=val[root];
    }

    void build()
    {
        int u=0,v;
        for(int i=0;i<26;++i){
            if(t[u][i])T.push(t[u][i]);
        }
        while(!T.empty()){
            u=T.front();T.pop();
            for(int i=0;i<26;++i){
                v=t[u][i];
                if(v){
                    fail[v]=t[fail[u]][i];
                    lis[v]=val[fail[v]]?fail[v]:lis[fail[v]];
                    T.push(v);
                }
                else t[u][i]=t[fail[u]][i];
            }
        }
    }
    
    void quary()
    {
        int u=0,v;
        for(int i=0;i<all;++i){
            if(s[i]=='#'){
                u=0;continue;
            }
            u=t[u][s[i]-'a'];
            if(val[u])ans[val[u]]++;
            v=lis[u];
            while(v){
                ans[val[v]]++;
                v=lis[v];
            }
        }
    }
}ac;

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;++i){
        scanf("%s",a);
        ac.ins(i);
    }
    ac.build();ac.quary();
    for(int i=1;i<=n;++i)
    printf("%d\n",ans[mk[i]]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/cytus/p/9457605.html