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

bzoj 3172 [Tjoi2013]单词(fail树,DP)

2016-02-20 08:56 627 查看
【题目链接】

  http://www.lydsy.com/JudgeOnline/problem.php?id=3172

【题意】

题目的意思是这样的,给若干个单词,求每个单词在这一堆单词中的出现次数。 出题人语文水平高

【思路】

  AC自动机. fail树

  AC自动机中的fail指针指向该串的一个后缀,将fail指针反向后得到一棵fail树,利用getFail后的bfs序在树上进行DP统计出现次数。

  在fail树上,父节点对应字符串是其子结点对应字符串的极大后缀。我们用sum[u]记录一个结点u被几个单词结点所经过,插入时顺便统计一下即可。设pos[i]为单词i在自动机上所对应的尾节点,那么这时候sum[pos[i]]是否为i的答案呢?不是。因为可能出现有一个字符串为abbabc,而i是abc的情况,这时候abc作为后缀出现但是并没有计数,对于结点u,我们应该将fail树上u->root路径上的所有节点的sum+=sum[u],这步操作只需要递推一下,这时候的sum[pos[i]]才是i的答案。

  感觉与SAM中的p=>p->fa的思路挺像的。

【代码】

#include<cstdio>
#include<cstring>
using namespace std;

const int N = 1e6+10;

struct ACauto {
int sz,ch
[26],sum
,q
,pos
,f
;
void init() {
sz=1;
memset(ch[0],0,sizeof(ch[0]));
}
void insert(char* s,int rank) {
int u=0;
for(int i=0;s[i];i++) {
int c=s[i]-'a';
if(!ch[u][c]) {
memset(ch[sz],0,sizeof(ch[sz]));
ch[u][c]=sz++;
}
u=ch[u][c];
sum[u]++;
}
pos[rank]=u;
}
void get_Fail() {
int front=1,rear=1;        //a pos for 0
f[0]=1; q[0]=1;
for(int i=0,p;i<26;i++)
if(p=ch[0][i]) f[p]=0,q[rear++]=p;
while(front!=rear) {
int qr=q[front++];
for(int c=0;c<26;c++) {
int u=ch[qr][c];
if(!u) continue;
q[rear++]=u; int v=f[qr];
while(v&&!ch[v][c]) v=f[v];
f[u]=ch[v][c];
}
}
for(int i=rear-1;i>=0;i--)
sum[f[q[i]]]+=sum[q[i]];
}
}ac;

int n;
char s
;

int main() {
scanf("%d",&n);
ac.init();
for(int i=1;i<=n;i++) {
scanf("%s",s);
ac.insert(s,i);
}
ac.get_Fail();
for(int i=1;i<=n;i++)
printf("%d\n",ac.sum[ac.pos[i]]);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: