码迷,mamicode.com
首页 > 编程语言 > 详细

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

时间:2015-08-27 18:50:12      阅读:387      评论:0      收藏:0      [点我收藏+]

标签:

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);
        }
    }  
}  


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

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

标签:

原文地址:http://blog.csdn.net/yu_ch_sh/article/details/48029429

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!