BZOJ 题目3172: [Tjoi2013]单词(AC自动机||AC自动机+fail树||后缀数组暴力||后缀数组+RMQ+二分等五种姿势水过)

3172: [Tjoi2013]单词

Time Limit: 10 Sec  Memory Limit: 512 MB

Submit: 1890  Solved: 877

[Submit][Status][Discuss]

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

一看这个题,感觉超级水,,就直接用ac自动机瞎搞了一下,然后跑了9000多ms过了,差点超时,吓哭了,然后百度人家都用后缀数组+RMQ或者ac自动机+fail树写的,fail树不懂是什么鬼,,就用后缀数组水了一下,把所有的串全部连到一个字符串,中间隔开,然后找每个出现的次数就行了,由后缀数组的性质可以知道,就是找height大于的长度向前最大向后最大的距离就是了,本想用RMQ和二分试试,感觉有点麻烦,就用暴力的试了试,样例对,提交过了,跑了1000多ms很不错,当感觉暴力是不是太慢了,就用RMQ+二分试了试,跑了2000多ms比暴力要慢,,但还是感觉可能数据弱的问题,第二种应该是比较好的写法,然后就去学了学fail树,实际上很简单就是把fail指针反向建成树,一直加到root,ac自动机数组版的反向加fail的值参照了一下HZW的,膜拜中学生啊~~~,跑了300多ms,果然碉堡了,,但是一直不习惯数组版的那种写法,有老老实实的写了一遍结构体的,建的fail树,感觉好理解多了,跑了400多ms,也不慢~~

ac代码

裸AC自动机搞

/**************************************************************
    Problem: 3172
    User: kxh1995
    Language: C++
    Result: Accepted
    Time:9228 ms
    Memory:314756 kb
****************************************************************/

#include<stdio.h>
#include<string.h>
#include<queue>
#include<iostream>
using namespace std;
const int  maxnode=1000000+10;
const int sg_size=27;
char str[200000010],key[1000010];
int pos[220];
struct Trie
{
    int ch[maxnode][sg_size];
    int val[maxnode];
    int f[maxnode];
    int num[maxnode];
    int sz;
    void init()
    {
        sz=1;
        memset(ch,0,sizeof(ch));
        memset(val,0,sizeof(val));
        memset(f,0,sizeof(f));
        memset(num,0,sizeof(num));
    }
    int idx(char c)
    {
        return c-'a';
    }
    int insert(char *s)
    {
        int u=0,i;
        for(i=0;s[i];i++)
        {
            int c=idx(s[i]);
            if(!ch[u][c])
                ch[u][c]=sz++;;
            u=ch[u][c];
        }
        val[u]++;
        num[u]=0;
        return u;
    }
    void build_ac()
    {
        queue<int>q;
        int i;
        for(i=0;i<sg_size;i++)
        {
            if(ch[0][i])
                q.push(ch[0][i]);
        }
        int r,c,u,v;
        while(!q.empty())
        {
            r=q.front();
            q.pop();
            for(c=0;c<sg_size;c++)
            {
                u=ch[r][c];
                if(!u)
                    continue;
                q.push(u);
                v=f[r];
                while(v&&ch[v][c]==0)
                    v=f[v];
                f[u]=ch[v][c];
            }
        }
    }
    void find(char *s)
    {
        int j=0;
        for(int i=0;s[i];i++)
        {
            int c=idx(s[i]);
            while(j&&ch[j][c]==0)
                j=f[j];
            j=ch[j][c];
            int temp=j;
            while(temp)
            {
                num[temp]++;
                temp=f[temp];
            }
        }
    }
}ac;
int main()
{
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        int i;
        ac.init();
        int len;
        for(i=1;i<=n;i++)
        {
            scanf("%s",key);
            pos[i]=ac.insert(key);
            strcat(str,key);
            len=strlen(str);
            str[len]='z'+1;
            str[len+1]='\0';
        }
        ac.build_ac();
        ac.find(str);
        for(i=1;i<=n;i++)
        {
            printf("%d\n",ac.num[pos[i]]);
        }
    }
}

后缀数组暴力版

/**************************************************************
    Problem: 3172
    User: kxh1995
    Language: C++
    Result: Accepted
    Time:1528 ms
    Memory:25712 kb
****************************************************************/

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#define min(a,b) (a>b?b:a)
#define max(a,b) (a>b?a:b)
#define N 1001000
using namespace std;
char str[N];
int sa[N],Rank[N],rank2[N],height[N],c[N],*x,*y,s[N],k;
void cmp(int n,int sz)
{
    int i;
    memset(c,0,sizeof(c));
    for(i=0;i<n;i++)
        c[x[y[i]]]++;
    for(i=1;i<sz;i++)
        c[i]+=c[i-1];
    for(i=n-1;i>=0;i--)
        sa[--c[x[y[i]]]]=y[i];
}
void build_sa(char *s,int n,int sz)
{
    x=Rank,y=rank2;
    int i,j;
    for(i=0;i<n;i++)
        x[i]=s[i],y[i]=i;
    cmp(n,sz);
    int len;
    for(len=1;len<n;len<<=1)
    {
        int yid=0;
        for(i=n-len;i<n;i++)
        {
            y[yid++]=i;
        }
        for(i=0;i<n;i++)
            if(sa[i]>=len)
                y[yid++]=sa[i]-len;
            cmp(n,sz);
        swap(x,y);
        x[sa[0]]=yid=0;
        for(i=1;i<n;i++)
        {
            if(y[sa[i-1]]==y[sa[i]]&&sa[i-1]+len<n&&sa[i]+len<n&&y[sa[i-1]+len]==y[sa[i]+len])
                x[sa[i]]=yid;
            else
                x[sa[i]]=++yid;
        }
        sz=yid+1;
        if(sz>=n)
            break;
    }
    for(i=0;i<n;i++)
        Rank[i]=x[i];
}
void getHeight(char *s,int n)
{
    int k=0;
    for(int i=0;i<n;i++)
    {
        if(Rank[i]==0)
            continue;
        k=max(0,k-1);
        int j=sa[Rank[i]-1];
        while(s[i+k]==s[j+k])
            k++;
        height[Rank[i]]=k;
    }
}
int l[220],r[220];
int main()
{
    int kk;
    while(scanf("%d",&kk)!=EOF)
    {
        int n=0;
        int i,j;
        for(i=1;i<=kk;i++)
        {
            scanf("%s",str+n);
            l[i]=n;
            n=strlen(str);
            r[i]=n-l[i];
            str[n++]='z'+1;
        }
        str[n]=0;
        build_sa(str,n+1,128);
        getHeight(str,n);
        for(i=1;i<=kk;i++)
        {
            int t=Rank[l[i]],len=r[i],rr,ll;
            for(j=t;j&&height[j]>=len;j--);
                ll=j;
            for(j=t+1;j<n&&height[j]>=len;j++);
                rr=j;
            printf("%d\n",rr-ll);
        }
    }
}

后缀数组+RMQ+二分版

/**************************************************************
    Problem: 3172
    User: kxh1995
    Language: C++
    Result: Accepted
    Time:2756 ms
    Memory:107824 kb
****************************************************************/

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#define min(a,b) (a>b?b:a)
#define max(a,b) (a>b?a:b)
#define N 1001000
using namespace std;
char str[N];
int sa[N],Rank[N],rank2[N],height[N],c[N],*x,*y,s[N],k;
void cmp(int n,int sz)
{
    int i;
    memset(c,0,sizeof(c));
    for(i=0;i<n;i++)
        c[x[y[i]]]++;
    for(i=1;i<sz;i++)
        c[i]+=c[i-1];
    for(i=n-1;i>=0;i--)
        sa[--c[x[y[i]]]]=y[i];
}
void build_sa(char *s,int n,int sz)
{
    x=Rank,y=rank2;
    int i,j;
    for(i=0;i<n;i++)
        x[i]=s[i],y[i]=i;
    cmp(n,sz);
    int len;
    for(len=1;len<n;len<<=1)
    {
        int yid=0;
        for(i=n-len;i<n;i++)
        {
            y[yid++]=i;
        }
        for(i=0;i<n;i++)
            if(sa[i]>=len)
                y[yid++]=sa[i]-len;
            cmp(n,sz);
        swap(x,y);
        x[sa[0]]=yid=0;
        for(i=1;i<n;i++)
        {
            if(y[sa[i-1]]==y[sa[i]]&&sa[i-1]+len<n&&sa[i]+len<n&&y[sa[i-1]+len]==y[sa[i]+len])
                x[sa[i]]=yid;
            else
                x[sa[i]]=++yid;
        }
        sz=yid+1;
        if(sz>=n)
            break;
    }
    for(i=0;i<n;i++)
        Rank[i]=x[i];
}
void getHeight(char *s,int n)
{
    int k=0;
    for(int i=0;i<n;i++)
    {
        if(Rank[i]==0)
            continue;
        k=max(0,k-1);
        int j=sa[Rank[i]-1];
        while(s[i+k]==s[j+k])
            k++;
        height[Rank[i]]=k;
    }
}
int minv[N][20],lg[N];
void init_lg()
{
    int i;
    lg[1]=0;
    for(i=2;i<N;i++)
    {
        lg[i]=lg[i>>1]+1;
    }
}
void init_RMQ(int n)
{
    int i,j,k;
    for(i=1;i<=n;i++)
    {
        minv[i][0]=height[i];
    }
    for(j=1;j<=lg[n];j++)
    {
        for(k=0;k+(1<<j)-1<=n;k++)
        {
            minv[k][j]=min(minv[k][j-1],minv[k+(1<<(j-1))][j-1]);
        }
    }
}
int lcp(int l,int r)
{
    if(l>r)
        swap(l,r);
    //l++;
    int k=lg[r-l+1];
    return min(minv[l][k],minv[r-(1<<k)+1][k]);
}
int l[220],r[220];
int main()
{
    int kk;
    while(scanf("%d",&kk)!=EOF)
    {
        int n=0;
        int i,j;
        for(i=1;i<=kk;i++)
        {
            scanf("%s",str+n);
            l[i]=n;
            n=strlen(str);
            r[i]=n-l[i];
            str[n++]='z'+1;
        }
        str[n]=0;
        build_sa(str,n+1,128);
        getHeight(str,n);
        init_lg();
        init_RMQ(n);
        for(i=1;i<=kk;i++)
        {
            int t=Rank[l[i]],len=r[i],rr,ll;
            int ml=0,mr=t;
            int ans=0,tot=1;
            while(ml<=mr)
            {
                int mid=(ml+mr)>>1;
                if(lcp(mid,t)>=len)
                {
                    ans=mid;
                    mr=mid-1;
                }
                else
                    ml=mid+1;
            }

            //ll=ans;
            //printf("*******%d\n",ll);
            if(ans)
                ans=t-ans+1;
            tot+=ans;
            ml=t+1,mr=n-1;
            ans=0;
            while(ml<=mr)
            {
                int mid=(ml+mr)>>1;
                if(lcp(t+1,mid)>=len)
                {
                    ans=mid;
                    ml=mid+1;
                }
                else
                    mr=mid-1;
            }
            if(ans)
                ans=ans-t;
            tot+=ans;
        //  rr=ans;
        //  printf("********%d\n",rr);
            printf("%d\n",tot);
        }
    }
}

AC自动机反向加fail的值数组版

/**************************************************************
    Problem: 3172
    User: kxh1995
    Language: C++
    Result: Accepted
    Time:324 ms
    Memory:115532 kb
****************************************************************/

#include<stdio.h>
#include<string.h>
#include<queue>
#include<iostream>
using namespace std;
const int  maxnode=1000000+10;
const int sg_size=26;
char key[1000010];
int pos[220];
struct Trie
{
    int ch[maxnode][sg_size];
    int f[maxnode];
    int sum[maxnode];
    int q[maxnode];
    int sz;
    void init()
    {
        sz=1;
    //  memset(ch,0,sizeof(ch));
        for(int i=0;i<26;i++)
            ch[0][i]=1;
        memset(f,0,sizeof(f));
        memset(sum,0,sizeof(sum));
    }
    int idx(char c)
    {
        return c-'a';
    }
    int insert(char *s)
    {
        int u=1,i;
        for(i=0;s[i];i++)
        {
            int c=idx(s[i]);
            if(!ch[u][c])
                ch[u][c]=++sz;
            u=ch[u][c];
            sum[u]++;
        }
        return u;
    }
    void build_ac()
    {
        int head=0;
        int tail=1;
        q[0]=1;
        while(head!=tail)
        {
            int r=q[head];
            head++;
            for(int c=0;c<sg_size;c++)
            {
                int u=ch[r][c];
                if(!u)
                    continue;
                q[tail++]=u;
                int v=f[r];
                while(v&&ch[v][c]==0)
                    v=f[v];
                f[u]=ch[v][c];
            }
        }
        for(int i=tail-1;i>=0;i--)
        {
            sum[f[q[i]]]+=sum[q[i]];
        }
    }  

}ac;
int main()
{
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        int i;
        ac.init();
        int len;
        for(i=1;i<=n;i++)
        {
            scanf("%s",key);
            pos[i]=ac.insert(key);
        }
        ac.build_ac();
        for(i=1;i<=n;i++)
        {
            printf("%d\n",ac.sum[pos[i]]);
        }
    }
}

ac自动机+fail树

/**************************************************************
    Problem: 3172
    User: kxh1995
    Language: C++
    Result: Accepted
    Time:452 ms
    Memory:229136 kb
****************************************************************/

#include<stdio.h>
#include<string>
#include<string.h>
#include<iostream>
using namespace std;
char key[1000001];
int head,tail;
int sz=0;
struct node
{
    node *fail;
    node *next[26];
    int cnt,num;
    node()
    {
        fail=NULL;
        cnt=0;
        for(int i=0;i<26;i++)
            next[i]=NULL;
        num=sz++;
    }
}*q[26000500],*p1[220];
struct Edge
{
    node *y;
    Edge *next;
}*b[26000100];
node *root;
void add(node *x,node *y)
{
    Edge *k=new Edge;
    k->y=y;
    k->next=b[x->num];
    b[x->num]=k;
}
void insert(char *s,int key)
{
    int temp,len,i;
    node *p=root;
    len=strlen(s);
    for(i=0;i<len;i++)
    {
        temp=s[i]-'a';
        if(p->next[temp]==NULL)
            p->next[temp]=new node();
        p=p->next[temp];
        p->cnt++;
    }
    p1[key]=p;
}
void build_ac()
{
    head=tail=0;
    q[tail++]=root;
    while(head!=tail)
    {
        node *p=q[head++];
        node *temp=NULL;
        for(int i=0;i<26;i++)
        {
            if(p->next[i]!=NULL)
            {
                if(p==root)
                {
                    p->next[i]->fail=root;
                    add(root,p->next[i]);
                }
                else
                {
                    temp=p->fail;
                    while(temp!=NULL)
                    {
                        if(temp->next[i]!=NULL)
                        {
                            p->next[i]->fail=temp->next[i];
                            add(temp->next[i],p->next[i]);
                            break;
                        }
                        temp=temp->fail;
                    }
                    if(temp==NULL)
                    {
                        p->next[i]->fail=root;
                        add(root,p->next[i]);
                    }
                }
                q[tail++]=p->next[i];
            }
        }
    }
}
void dfs(node *fa)
{
    Edge *p;
    for(p=b[fa->num];p!=NULL;p=p->next)
    {
        dfs(p->y);
        fa->cnt+=p->y->cnt;
    }
}
int main()
{
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        head=tail=0;
        sz=0;
        root=new node();
        int i;
        for(i=1;i<=n;i++)
        {
            scanf("%s",key);
            insert(key,i);
        }
        build_ac();
        dfs(root);
        for(i=1;i<=n;i++)
        {
            printf("%d\n",p1[i]->cnt);
        }
    }
}  

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-10 10:24:26

BZOJ 题目3172: [Tjoi2013]单词(AC自动机||AC自动机+fail树||后缀数组暴力||后缀数组+RMQ+二分等五种姿势水过)的相关文章

[BZOJ 3172] [Tjoi2013] 单词 【AC自动机】

题目链接:BZOJ - 3172 题目分析: 题目要求求出每个单词出现的次数,如果把每个单词都在AC自动机里直接跑一遍,复杂度会很高. 这里使用AC自动机的“副产品”——Fail树,Fail树的一个性质是,一个字符串出现的次数,就等于以它的结点为根的Fail树中的子树中所有结点的 Cnt 和. 所以把每个单词插入的时候每个字符都 ++Cnt ,在建 Fail 的时候将结点依次压入一个栈,最后再从栈顶开始弹栈,更新栈顶元素的 Fail 的 Cnt 值,这样就是自叶子节点向上更新了. 我开始写的时候

BZOJ 3172: [Tjoi2013]单词 [AC自动机 Fail树]

3172: [Tjoi2013]单词 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 3198  Solved: 1532[Submit][Status][Discuss] Description 某人读论文,一篇论文是由许多单词组成.但他发现一个单词会在论文中出现很多次,现在想知道每个单词分别在论文中出现多少次. Input 第一个一个整数N,表示有多少个单词,接下来N行每行一个单词.每个单词由小写字母组成,N<=200,单词长度不超过10^6

BZOJ 3172: [Tjoi2013]单词 AC自动机

3172: [Tjoi2013]单词 Description 某人读论文,一篇论文是由许多单词组成.但他发现一个单词会在论文中出现很多次,现在想知道每个单词分别在论文中出现多少次. Input 第一个一个整数N,表示有多少个单词,接下来N行每行一个单词.每个单词由小写字母组成,N<=200,单词长度不超过10^6 Output 输出N个整数,第i行的数字表示第i个单词在文章中出现了多少次. Sample Input 3 a aa aaa Sample Output 6 3 1 HINT 入门 #

BZOJ 3172: [Tjoi2013]单词 &&  BZOJ 2434 [Noi2011]阿狸的打字机 (施工中)

fail树 链接地址 定义 把所有fail指针逆向,这样就得到了一棵树 (因为每个节点的出度都为1,所以逆向后每个节点入度为1,所以得到的是一棵树) 还账… 有了这个东西,我们可以做很多事… 对于AC自动机的构造前面的文章已经讲了,而在查询的时候,有一点感觉没有说清楚: 对于x串在y串中出现,必然是在y串某个前缀的后缀与x串相同 fail指针指向与该节点表示串后缀相等的且长度最大的串(或前缀)的节点 然后,根据fail指针的原理,在查询的时候,沿着当前节点的fail指针向上查找,直到root结束

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

3172: [Tjoi2013]单词 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 2327  Solved: 1093[Submit][Status][Discuss] Description 某人读论文,一篇论文是由许多单词组成.但他发现一个单词会在论文中出现很多次,现在想知道每个单词分别在论文中出现多少次. Input 第一个一个整数N,表示有多少个单词,接下来N行每行一个单词.每个单词由小写字母组成,N<=200,单词长度不超过10^6

3172: [Tjoi2013]单词

3172: [Tjoi2013]单词 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 1424  Solved: 653[Submit][Status] Description 某人读论文,一篇论文是由许多单词组成.但他发现一个单词会在论文中出现很多次,现在想知道每个单词分别在论文中出现多少次. Input 第一个一个整数N,表示有多少个单词,接下来N行每行一个单词.每个单词由小写字母组成,N<=200,单词长度不超过10^6 Output 输出N

BZOJ 3172 Tjoi2013 单词 fail树

题目大意及后缀数组做法见 http://blog.csdn.net/popoqqq/article/details/41042473 原来正解是fail树--难怪后缀数组被卡成这样 首先我们将给出的n个串构建AC自动机 朴素的做法是对于每个串将这个串每个节点沿着fail指针扫一遍,将路径上的所有点的cnt++ 但是这样做会TLE 我们不妨反向思考 fail指针反向后是一棵树 沿着fail指针扫一遍就是沿着树边向根扫一遍 只在插入时将每个串的每个节点cnt++ 那么每个串终点所在fail树的子树中

[NOI2011][bzoj2434] 阿狸的打字机 [AC自动机+dfs序+fail树+树状数组]

题面 传送门 正文 最暴力的 最暴力的方法:把所有询问代表的字符串跑一遍kmp然后输出 稍微优化一下:把所有询问保存起来,把模板串相同的合并,求出next然后匹配 但是这两种方法本质没有区别,都是暴力 不那么暴力的 我们对于所有的串建立一个AC自动机,把询问按照$y$排序,然后在AC自动机上面跑,每次跳fail更新答案 这样可以拿到70分,但是时间上限还是会$O\left(n^2\right)$左右 巧妙的优化 这道题里面,所有的模板串和文本串都在AC自动机里 那么,题目中实际是在要求什么呢?

bzoj 3172: [Tjoi2013]单词

1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #define M 1000008 5 using namespace std; 6 char ch[M]; 7 int a[M][27],n,cnt=1,fa[M],f1[M],ans[M],q[M]; 8 long long sum[M],aa; 9 void jia() 10 { 11 int now=1; 12 for(int i=1;i&