P3966 [TJOI2013]单词

传送门

AC自动机

一看就知道是AC自动机

它问的是每一个单词在所有单词中出现的次数

对所有单词搞一个AC自动机

然后把所有单词连在一起,中间用一个不会出现的字符或者其他什么的隔开

然后把连在一起的串放到AC自动机上搞匹配就行了

要注意单词可能有重复

所以要把重复的单词判一下

具体看代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
const int N=1e6+7;
int n,mp[207];
char s[N];
int c[N][27],pd[N],fail[N],g[N],cnt;
char a[N]; int b[N+207],len;
inline void ins(int num)//插入单词
{
    int u=0,l=strlen(a);
    for(int i=0;i<l;i++)
    {
        int v=a[i]-'a'+1;
        b[++len]=v;//把匹配串处理一下
        if(!c[u][v]) c[u][v]=++cnt;
        u=c[u][v];
    }
    if(!pd[u]) pd[u]=num;
    mp[num]=pd[u];//用来处理重复的单词,mp[i]表示第i个单词最早出现时的位置
    b[++len]=-1;//单词之间用-1隔开
}
queue <int> q;
inline void pre()//预处理fail和g,g[x]表示从x一直走失配边,走到的第一个结束标记的位置
{
    for(int i=1;i<=26;i++)
        if(c[0][i]) q.push(c[0][i]);
    while(!q.empty())
    {
        int u=q.front(); q.pop();
        for(int i=1;i<=26;i++)
        {
            int v=c[u][i];
            if(!v) c[u][i]=c[fail[u]][i];
            else
            {
                fail[v]=c[fail[u]][i];
                g[v]= pd[fail[v]] ? fail[v] : g[fail[v]];
                q.push(v);
            }
        }
    }
}
int ans[207];
inline void query()//匹配
{
    int u=0;
    for(int i=1;i<=len;i++)
    {
        if(b[i]==-1)//如果走到分隔标记,就回到起点匹配
        {
            u=0;
            continue;
        }
        u=c[u][b[i]];
        if(pd[u]) ans[pd[u]]++;
        for(int j=g[u];j;j=g[j])
            ans[pd[j]]++;
    }
    for(int i=1;i<=n;i++)
        printf("%d\n",ans[mp[i]]);
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",a);
        ins(i);
    }
    pre();
    query();
    return 0;
}

 

posted @ 2018-09-19 09:51  LLTYYC  阅读(217)  评论(0编辑  收藏  举报