牛客 [TJOJ2013] 单词 AC自动机变式

链接:https://ac.nowcoder.com/acm/problem/20443
来源:牛客网
 

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

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

输入描述:

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

输出描述:

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

示例1

输入

复制3 a aa aaa

3
a
aa
aaa

输出

复制

6
3
1

题目分析:

卡了我好一会儿,最后错在单词可能重复,but最后T了。

看了一下题解发现妙哉!AC自动机fail树上跑dp即可。

假设考虑 一个字符串s在另一个字符串中出现的次数比如 样例中a在aaa中出现的次数,首先a可以作为前缀,所以构造aaa时第1个位置就需要+1,其次a可以作为aaa中的最长相同前后缀,比如a可以作为aa的最长相同前后缀,这就说明aa可以贡献一个a。那么问题又转换成了aa的出现次数,aa首先出现作为前缀一次,其次又是aaa的最长相同前后缀,所以为2次,说明aaa可以贡献两个aa,三个a。由此可以看出,这是个fail树上的dp,dp[i]代表fail树上以i结尾的单词在整个字典库中出现了多少次,那么按层最底层向第一层dp:sz[fail[u]]+=sz[u]。

原理就是一个串在另一个串中的出现次数可以由前缀+后缀次数组成。

这个可以举例自证:

aba 在abababa中的出现次数:

首先作为前缀出现一次。

之后ababa是最长相同前后缀,ababa就会出现2次,一次作为前缀一次作为后缀

之后aba是最ababa的长相同前后缀,同样的此时aba存在两次ababa的后缀所以后缀次数是2

过程中不会重复原因:

abababa->ababa(2)->aba(3)我们每次加入的都是上一个字符串后缀aba的数量,因为每次fail指针后,字符串长度会缩小,也就相当于终点会改变,所以不会重复。并且前缀的数量不会重复,因为aba不可能具有aba的最长相同前后缀!

/*** keep hungry and calm CoolGuang!***/
#pragma GCC optimize(2)
#include <bits/stdc++.h>
#include<stdio.h>
#include<algorithm>
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long long ll;
typedef pair<int,int> pp;
const ll INF=1e18;
const int maxn=1e6+18;
const int mod=19260817;
inline bool read(ll &num)
{char in;bool IsN=false;in=getchar();if(in==EOF) return false;while(in!='-'&&(in<'0'||in>'9')) in=getchar();if(in=='-'){ IsN=true;num=0;}else num=in-'0';while(in=getchar(),in>='0'&&in<='9'){num*=10,num+=in-'0';}if(IsN) num=-num;return true;}
ll n,m,p;
int res[maxn];
struct Acaho{
    int nxt[maxn][26];
    int rt=0;///
    int id[maxn];
    int cot[maxn];
    int fail[maxn];
    ll cnt;
    int number=0;
    void inint(){
        cnt=0;
        number=0;
        memset(fail,0,sizeof(fail));
        memset(id,0,sizeof(id));
        memset(cot,0,sizeof(cot));
        memset(nxt,0,sizeof(nxt));
        memset(res,0,sizeof(res));
    }
    void Insert(char *p){
        int len=strlen(p);
        number++;
        int rt=0;
        for(int i=0;i<len;i++){
            int x=p[i]-'a';
            if(!nxt[rt][x]) nxt[rt][x]=++cnt;
            rt=nxt[rt][x];
            cot[rt]++;
        }
        res[number]=rt;
    }
    void GetFail(){
        queue<int>q;
        while(!q.empty()) q.pop();
        fail[0]=-1;
        q.push(0);
        int y=0;
        while(!q.empty()){
            int u=q.front();q.pop();
            for(int k=0;k<26;k++){
                if(nxt[u][k]){
                    id[++y]=nxt[u][k];
                    int v=fail[u];
                    while(v!=-1&&nxt[v][k]==0) v=fail[v];
                    if(v==-1) fail[nxt[u][k]]=0;
                    else fail[nxt[u][k]]=nxt[v][k];
                    q.push(nxt[u][k]);
                }
            }
        }
    }
    void Find(){
        for(int i=cnt;i>=1;i--) cot[fail[id[i]]]+=cot[id[i]];
        for(int i=1;i<=n;i++)
            printf("%d\n",cot[res[i]]);
    }
}aho;
char str[202][maxn];
int main()
{
    aho.inint();
    scanf("%lld",&n);
    for(int i=1;i<=n;i++){
        scanf("%s",str[i]);
        aho.Insert(str[i]);
    }
    aho.GetFail();
    aho.Find();
    return 0;
}
/***
6
1 2
1 3
2 4
2 5
5 6

***/
发布了157 篇原创文章 · 获赞 146 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_43857314/article/details/104886211