• BZOJ3172: [Tjoi2013]单词


    【传送门:BZOJ3172


    简要题意:

      给出n个单词,你可以理解为将这些单词变成一个个段落,然后求出每个单词在所有段落中出现的次数


    题解(一):

      刚开始不是很懂题目,结果发现将所有单词看成一篇文章,每个单词看成一个段落就懂了

      由于某种unbelievable的原因,我刚好做了AC自动机的专题训练,看到这道题就秒想AC自动机

      将每个单词放进AC自动机里,每个点的s表示有多少个单词经过,然后在构建失败指针的时候,通过队列来更新s值,怎么更新呢,假设有一个i点,它的失败指针指向j,设sj为从根到j点所构成的字符串,si为从根到i点所构成的字符串,那么我们可以知道sj为si的后缀,也说明了si中有sj的出现,那么就将j点的s值加上i点的s值,达到更新且不重复的目的来求出答案


     

    参考代码(一):

    #include<queue>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cstdlib>
    #include<cmath>
    using namespace std;
    struct node
    {
        int s,c[27],fail;
        node()
        {
            s=fail=0;
            memset(c,-1,sizeof(c));
        }
    }t[1100000];
    int tot,ans,n;
    char a[1100000];
    int ed[210];
    void bt(int k,int root)
    {
        int x=root,len=strlen(a+1);
        for(int i=1;i<=len;i++)
        {
            int y=a[i]-'a'+1;
            if(t[x].c[y]==-1)
            {
                t[x].c[y]=++tot;
            }
            x=t[x].c[y];
            t[x].s++;
        }
        ed[k]=x;
    }
    int list[1100000];
    void bfs()
    {
        int x;
        int head=1,tail=1;
        list[1]=0;
        while(head<=tail)
        {
            x=list[head];
            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);
                    int x=t[son].fail,y=son;
                }
                list[++tail]=son;
            }
            head++;
        }
        for(int i=tail;i>=1;i--) t[t[list[i]].fail].s+=t[list[i]].s;
    }
    int main()
    {
        ans=tot=0;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%s",a+1);
            bt(i,0);
        }
        bfs();
        for(int i=1;i<=n;i++)
        {
            printf("%d
    ",t[ed[i]].s);
        }
        return 0;
    }

     


     

    题解(二):

        A了这道题之后才发现早在之前就A了(心酸)

      那我们就来搞一下第二种想法

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

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

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

      由于trie树上的每个点相当于一个字符串,所以这棵fail树父亲节点是儿子节点所构成字符串的最长后缀

      这样子对于这道题而言,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
    ",s[end[i]]);
        return 0;
    }

     

  • 相关阅读:
    机器学习(八) 多项式回归与模型泛化(上)
    机器学习(七) PCA与梯度上升法 (下)
    机器学习(七) PCA与梯度上升法 (上)
    机器学习(六) 梯度下降法 (下)
    机器学习(六) 梯度下降法 (上)
    机器学习(五) 线性回归法 (下)
    机器学习(五) 线性回归法 (上)
    机器学习(四) 机器学习(四) 分类算法--K近邻算法 KNN (下)
    获取字符串中img标签的url集合(转载)
    微信上传文章素材—ASP.NET MVC从View层传数据到Controller层
  • 原文地址:https://www.cnblogs.com/Never-mind/p/7628734.html
Copyright © 2020-2023  润新知