BZOJ3172: [Tjoi2013]单词

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ3172: [Tjoi2013]单词相关的知识,希望对你有一定的参考价值。

【传送门:BZOJ3172


简要题意:

  给出n个字符串,求出每个字符串在这n个字符串中出现的次数


题解:

  很容易就想到AC自动机,但是单单是AC自动机还不行

  这时就要用AC自动机的延伸——fail树来做(时常膜一膜算法,有益身体健康)

  fail树这个玩意就是将AC自动机中fail指针当成是一条边,然后建成一棵树

  由于trie树上的每个点相当于一个字符串,所以这棵fail树满足假如有一个x点,x点的字符串一定是x点的子树中所有节点的字符串中的最长后缀(最长后缀也就是说明,字典树中在所有能够成为这些节点的后缀字符串长度,x点的字符串的长度最长)

  这样子对于这道题而言,fail树简直就是神一般的存在QAQ

  首先将每个字符串放进trie树里面,并且每经过一个trie树里的点,就用s数组记录这个字符串出现的个数,每经过一次就加一,然后构造AC自动机,然后在构造AC自动机的过程中,对每个点的fail指针进行建边,然后用dfs从根往下遍历,把s数组从下往上累加

  然后在输入每个字符串的时候,记录每个字符串的最后一个字符在trie树上的编号,然后一个一个输出s[end[i]](end[i]表示第i个字符串最后一个字母在trie树上的编号)


参考代码:

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<cmath>
using namespace std;
struct node
{
    int c[27],fail;
    node()
    {
        fail=0;
        memset(c,-1,sizeof(c));
    }
}t[1100000];
int tot,n;
char st[1100000];
int s[1100000];
int end[1100000];
void bt(int root,int z)
{
    int x=root,len=strlen(st+1);
    for(int i=1;i<=len;i++)
    {
        int y=st[i]-a+1;
        if(t[x].c[y]==-1)
        {
            t[x].c[y]=++tot;
        }
        x=t[x].c[y];s[x]++;
    }
    end[z]=x;
}
struct edge
{
    int x,y,next;
}a[1100000];int len,last[1100000];
void ins(int x,int y)
{
    len++;
    a[len].x=x;a[len].y=y;
    a[len].next=last[x];last[x]=len;
}
queue<int> q;
void bfs()
{
    int x;
    q.push(0);
    while(q.empty()==0)
    {
        x=q.front();
        for(int i=1;i<=26;i++)
        {
            int son=t[x].c[i];
            if(son==-1)continue;
            if(x==0) t[son].fail=0;
            else
            {
                int j=t[x].fail;
                while(j!=0&&t[j].c[i]==-1) j=t[j].fail;
                t[son].fail=max(t[j].c[i],0);
            }
            ins(t[son].fail,son);
            q.push(son);
        }
        q.pop();
    }
}
void dfs(int x)
{
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        dfs(y);
        s[x]+=s[y];
    }
}
int main()
{
    tot=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",st+1);
        bt(0,i);
    }
    len=0;memset(last,0,sizeof(last));
    bfs();
    dfs(0);
    for(int i=1;i<=n;i++) printf("%d\n",s[end[i]]);
    return 0;
}

 

以上是关于BZOJ3172: [Tjoi2013]单词的主要内容,如果未能解决你的问题,请参考以下文章

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

[Bzoj3172][Tjoi2013]单词(fail树)

bzoj3172 [Tjoi2013]单词

BZOJ 3172 [Tjoi2013]单词

bzoj 3172: [Tjoi2013]单词

BZOJ-3172: [Tjoi2013]单词 (AC自动姬)