【BZOJ3172】单词(AC自动机)

【BZOJ3172】单词(AC自动机)

题面

Description

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

Input

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

Output

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

Sample Input

3

a

aa

aaa

Sample Output

6

3

1

题解

yyb因为调这道题而死亡

在重复一下题目的意思把。。。

看不见文章对不读,,

那是因为文章就是所有单词组成的

然后你就可以yy所有单词中间有一个空格之类的东西

很明显的AC自动机,

然后,我们

每次把每个单词带进去匹配一下

暴跳fail指针

美滋滋的收获90分

【BZOJ3172】单词(AC自动机)

0分是空间玩炸了。。。

暴跳fail指针是可以被卡炸的。。。

所以,我们就不暴跳了呀

每次要跳的时候就在这个点这里打一个标记

所有标记打完之后

我们就从底下往上一层层跳(记一下bfs序倒着跳)

每次就只跳一层,然后标记丢到上面去

这样就可以一起跳啦

然后美滋滋的 AC啦

对了,

这题还有一点

就是会有重复的单词。。。

所以要记录一下每个单词和哪个单词是一样的(不是并查集)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define MAX 1100000
struct Node
{
int vis[26];
int fail,ff;
int id,sum;
}t[MAX];
int tot,lid[500];
int ans[500];
int n,q[MAX],tp;
char ch[MAX],ss[MAX];
void Insert(int id,char *s)
{
int gg=strlen(s);
int now=0;
for(int i=0;i<gg;++i)
{
if(!t[now].vis[s[i]-'a'])
t[now].vis[s[i]-'a']=++tot;
t[t[now].vis[s[i]-'a']].ff=now;
now=t[now].vis[s[i]-'a'];
}
if(!t[now].id)t[now].id=id,lid[id]=id;
else lid[id]=t[now].id;
}
queue<int> Q;
void GetFail()
{
for(int i=0;i<26;++i)
if(t[0].vis[i])
Q.push(q[++tp]=t[0].vis[i]);
while(!Q.empty())
{
int u=Q.front();Q.pop();
for(int i=0;i<26;++i)
{
if(t[u].vis[i])
t[t[u].vis[i]].fail=t[t[u].fail].vis[i],Q.push(q[++tp]=t[u].vis[i]);
else
t[u].vis[i]=t[t[u].fail].vis[i];
}
}
}
int l=0;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
scanf("%s",ch);
Insert(i,ch);
int len=strlen(ch);
for(int j=0;j<len;++j)
ss[l++]=ch[j];
ss[l++]='#';
}
GetFail();
int now=0;
for(int j=0;j<l;++j)
{
if(ss[j]=='#')now=0;
else now=t[now].vis[ss[j]-'a'];
t[now].sum++;
}
for(int i=tp;i;i--)
{
ans[t[q[i]].id]+=t[q[i]].sum;
t[t[q[i]].fail].sum+=t[q[i]].sum;
}
for(int i=1;i<=n;++i)printf("%d\n",ans[lid[i]]);
return 0;
}
上一篇:CentOS7安装mongoDB数据库


下一篇:jvm常用参数设置 专题