[省选前题目整理][BZOJ 3172][TJOI 2013]单词(AC自动机+fail树)

来源:互联网 发布:枪林弹雨刷枪软件视频 编辑:程序博客网 时间:2024/05/18 02:08

题目链接

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

思路

首先将所有单词都插入AC自动机的Trie树中,记录下每个单词的危险节点(最后一个字符对应的节点)在Trie树中的编号。可以把Trie树中每个结点的fail指针看成一条边,fail指针指向的节点看成新树中这个节点的父亲,这就构成了fail树。我们记录下每个结点i在插入单词过程中被访问的次数f[i],那么在fail树中,对于每个点u及其儿子v而言,f[u]=f[u]+∑f[vi],vi是u的儿子(u代表文章的一个前缀,其fail指针所指向的点代表这个前缀的一个后缀,显然这个后缀的出现次数要算上它所在的所有前缀的出现次数)。那么单词i的出现次数就是i的危险节点的f值。

代码

#include <iostream>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <algorithm>#define MAXN 1000000using namespace std;int pos[MAXN]; //pos[i]=编号为i的单词的危险结点在trie中的编号struct Trie{    int ch[26],fail;    int cnt; //该节点在匹配时被遍历过的次数}node[MAXN];int nCount=0;void Insert(char *s,int id) //将编号为id的串s插入trie{    int p=0;    while(*s!='\0')    {        if(!node[p].ch[*s-'a'])            node[p].ch[*s-'a']=++nCount;        p=node[p].ch[*s-'a'];        s++;        node[p].cnt++;    }    pos[id]=p;}int n; //n个单词char word[MAXN];void init(){    scanf("%d",&n);    for(int i=1;i<=n;i++)    {        scanf("%s",word);        Insert(word,i);    }}int q[MAXN],h=0,t=0;void buildAC(){    for(int i=0;i<26;i++)        if(node[0].ch[i])        {            node[node[0].ch[i]].fail=0;            q[t++]=node[0].ch[i];        }    while(h<t)    {        int now=q[h++];        for(int i=0;i<26;i++)        {            if(node[now].ch[i])            {                int tmp=node[now].fail;                while(tmp!=0&&!node[tmp].ch[i])                    tmp=node[tmp].fail;                if(node[tmp].ch[i])                    tmp=node[tmp].ch[i];                node[node[now].ch[i]].fail=tmp;                q[t++]=node[now].ch[i];            }        }    }}void solve(){    while(t)    {        t--;        node[node[q[t]].fail].cnt+=node[q[t]].cnt;    }}int main(){    init();    buildAC();    solve();    for(int i=1;i<=n;i++)        printf("%d\n",node[pos[i]].cnt);    return 0;}
0 0
原创粉丝点击