您的位置:首页 > 大数据 > 人工智能

【bzoj3172】[Tjoi2013]单词 AC自动机+fail树

2016-02-24 19:50 411 查看

Description

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

Input

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

Output

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

Sample Input

3

a

aa

aaa


Sample Output

6

3

1


HINT

Source

fail树太神了…

fail[u]所代表的串,其实是u所代表的串的后缀,因为fail[u]可能很多,所以把边反向成一棵树,若点a指向点b,表明a所代表的串是b所代表的串的后缀,也就是出现了一次。

这样把每个串的每个字符所指向的点(也就是每个串的代表前缀的所有点)的权值都加1,表示这个串出现了一次,那么建出fail树,第i个串的答案就是第i个串结尾的点的标号的子树权值和…

关于代码实现,可以用逆拓扑序更新每个点的fail…Orzhzwer,手打队列还能这么玩…

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<stack>
using namespace std;

const int SZ = 1000010;

int ch[SZ][30],val[SZ],sz = 0;

int fail[SZ];

int d[SZ],id[SZ];

void insert(char s[],int num)
{
int p = 0;
int l = strlen(s);
for(int i = 0;i < l;i ++)
{
int c = s[i] - 'a' + 1;
if(!ch[p][c]) ch[p][c] = ++ sz;
p = ch[p][c];
d[p] ++;
}
val[p] ++;
id[num] = p;
}

queue<int> q;
stack<int> S;

void build_ac()
{
fail[0] = 0;
for(int i = 1;i <= 26;i ++)
{
int u = ch[0][i];
if(u)
{
fail[u] = 0;
q.push(u);
}
}
while(q.size())
{
int f = q.front(); q.pop();
S.push(f);
for(int i = 1;i <= 26;i ++)
{
int u = ch[f][i];
if(!u) continue;
q.push(u);
int v = fail[f];
while(v && !ch[v][i]) v = fail[v];
fail[u] = ch[v][i];
}
}
}

char s[SZ];

int main()
{
int n;
scanf("%d",&n);
for(int i = 1;i <= n;i ++)
{
scanf("%s",s);
insert(s,i);
}
build_ac();

while(S.size())
{
int x = S.top(); S.pop();
d[fail[x]] += d[x];
}

for(int i = 1;i <= n;i ++)
printf("%d\n",d[id[i]]);

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: