zoukankan      html  css  js  c++  java
  • BZOJ 2754([SCOI2012]喵喵叫的星球-统计序列的后缀阵列中子序列出现次数)

    2754: [SCOI2012]喵喵叫的星球

    Time Limit: 20 Sec  Memory Limit: 128 MB
    Submit: 805  Solved: 380
    [Submit][Status][Discuss]

    Description

    a180285幸运地被选做了地球到喵星球的留学生。他发现喵星人在上课前的点名现象非常有趣。

       如果课堂上有N个喵星人,每一个喵星人的名字由姓和名构成。喵星球上的老师会选择M个串来点名。每次读出一个串的时候,如果这个串是一个喵星人的姓或名的子串,那么这个喵星人就必须答到。 然而,因为喵星人的字码过于古怪,以至于不能用ASCII码来表示。为了方便描写叙述,a180285决定用数串来表示喵星人的名字。

    如今你能帮助a180285统计每次点名的时候有多少喵星人答到,以及M次点名结束后每一个喵星人答到多少次吗?  

    Input

     
    如今定义喵星球上的字符串给定方法:
    先给出一个正整数L,表示字符串的长度,接下来L个整数表示字符串的每一个字符。
    输入的第一行是两个整数N和M。
    接下来有N行。每行包括第i 个喵星人的姓和名两个串。姓和名都是标准的喵星球上的
    字符串。
    接下来有M行,每行包括一个喵星球上的字符串,表示老师点名的串。

    Output

     
    对于每一个老师点名的串输出有多少个喵星人应该答到。
    然后在最后一行输出每一个喵星人被点到多少次。

    Sample Input

    2 3
    6 8 25 0 24 14 8 6 18 0 10 20 24 0
    7 14 17 8 7 0 17 0 5 8 25 0 24 0
    4 8 25 0 24
    4 7 0 17 0
    4 17 0 8 25

    Sample Output


    2
    1
    0
    1 2
    【提示】
    其实例子给出的数据假设翻译成地球上的语言能够这样来看
    2 3
    izayoi sakuya
    orihara izaya
    izay
    hara
    raiz

    HINT



    【数据范围】 

     对于30%的数据,保证: 

    1<=N,M<=1000。喵星人的名字总长不超过4000。点名串的总长不超过2000。



    对于100%的数据,保证:

    1<=N<=20000,1<=M<=50000。喵星人的名字总长和点名串的总长分别不超过100000,保证喵星人的字符串中作为字符存在的数不超过10000。

    Source


    这题是后缀数组。

    先把模式串P写成s11*s12*s21*s22...sn2 (*为特殊字符)

    然后用find统计sa中出现上下界 平均O(Len总logLen总+MlogLen总) 最坏O(MLen总)

    可能会T但正解至今仍不知道


    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<functional>
    #include<iostream>
    #include<cmath>
    #include<cctype>
    #include<ctime>
    using namespace std;
    #define For(i,n) for(int i=1;i<=n;i++)
    #define Fork(i,k,n) for(int i=k;i<=n;i++)
    #define Rep(i,n) for(int i=0;i<n;i++)
    #define ForD(i,n) for(int i=n;i;i--)
    #define RepD(i,n) for(int i=n;i>=0;i--)
    #define Forp(x) for(int p=pre[x];p;p=next[p])
    #define Forpiter(x) for(int &p=iter[x];p;p=next[p])  
    #define Lson (x<<1)
    #define Rson ((x<<1)+1)
    #define MEM(a) memset(a,0,sizeof(a));
    #define MEMI(a) memset(a,127,sizeof(a));
    #define MEMi(a) memset(a,128,sizeof(a));
    #define INF (2139062143)
    #define F (100000007)
    #define MAXN (1000000+10)
    #define Sigma_size (10000+10)
    #define sp_char (10001)
    typedef long long ll;
    ll mul(ll a,ll b){return (a*b)%F;}
    ll add(ll a,ll b){return (a+b)%F;}
    ll sub(ll a,ll b){return (a-b+(a-b)/F*F+F)%F;}
    void upd(ll &a,ll b){a=(a%F+b%F)%F;}
    // 约定 -1:/0	sp_char:*	cat[i]=0 该位置无猫 
    class SA
    {
    public:
    	int s[MAXN];
    	int sa[MAXN],t[MAXN],t2[MAXN],c[MAXN],n;	
    	SA(){}
    	SA(int *_s,int _n){memcpy(s,_s,sizeof(s));n=_n;MEM(sa) MEM(t) MEM(t2) MEM(c) }
    	void mem(int *_s,int _n){memcpy(s,_s,sizeof(s));n=_n;MEM(sa) MEM(t) MEM(t2) MEM(c) }
    	void build_sa(int m)
    	{
    		int *x=t,*y=t2;
    		Rep(i,m) c[i]=0;
    		Rep(i,n) c[x[i]=s[i]]++; //x[i] 第i个字符串第一keyword排第几 
    		For(i,m-1) c[i]+=c[i-1];
    		RepD(i,n-1) sa[--c[x[i]]]=i;
    		for(int k=1;k<=n;k<<=1)
    		{
    			int p=0;
    			Fork(i,n-k,n-1) y[p++]=i;
    			Rep(i,n) if (sa[i]>=k) y[p++]=sa[i]-k; //y[i] 第二keyword排i的是第?字符串 
    			
    			Rep(i,m) c[i]=0;
    			Rep(i,n) c[x[y[i]]]++; // x[y[i]] 第二keyword排i的字符串,第1keyword排第几 
    			For(i,m-1) c[i]+=c[i-1];
    			RepD(i,n-1) sa[--c[x[y[i]]]]=y[i]; //第二keyword从大到小遍历。以第一keyword排序 ,最后得到以1,2keyword排序的 
    			//此时sa为第一keyword。第2keyword均排好序的 
    			swap(x,y);  //此时y变为第一keyword排序的rank,目标是把x变成第一,二keyword排序的rank 
    			p=1; x[sa[0]]=0;
    			For(i,n-1)
    				x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i]+k]==y[sa[i-1]+k] ? p-1:p++;
    			if (p>=n) break;
    			m=p;
    		}
    	}
    	int m; //模板串P的长度要事先赋值 
    	int cmp_suffix(int *pattern,int p)
    	{
    		for(int i=0;i<m;i++)
    		{
    			if (pattern[i]!=s[sa[p]+i]) return pattern[i]-s[sa[p]+i];
    		}
    		return 0; 
    	}
    	
    	int find(int *P,int _m)	
    	{
    		m=_m; //这里赋值也行 
    		if (cmp_suffix(P,0)<0||cmp_suffix(P,n-1)>0) return -1;
    		int L=0,R=n-1;
    		while(L<=R)
    		{
    			int M=(L+R)>>1;
    			int res=cmp_suffix(P,M);
    			if (!res) return M;
    			else if (res<0) R=M-1;
    			else L=M+1;
    		}
    		return -1;
    	}
    	int rank[MAXN],height[MAXN];
    	void make_height()
    	{
    		int k=0;
    		Rep(i,n) rank[sa[i]]=i;
    		Rep(i,n)
    		{
    			if (rank[i]-1<0) continue;
    			if (k) k--;
    			int j=sa[rank[i]-1];
    			while(s[i+k]==s[j+k]) ++k;
    			height[rank[i]]=k;
    		}
    	}
    }S;
    int flag[MAXN]={0},cat[MAXN]={0},sum[MAXN]={0};
    int len,s[MAXN];
    int n,m;
    int get_init(int &p,int i,bool is_cat)
    {
    	int len;
    	scanf("%d",&len);
    	while(len--)
    	{
    		scanf("%d",&s[++p]);
    		if (is_cat) cat[p]=i;
    	}
    	return len; 
    }
    int main()
    {
    //	freopen("bzoj2754.in","r",stdin);
    //	freopen(".out","w",stdout);
    	
    	scanf("%d%d",&n,&m);
    	
    	int p=-1;
    	For(i,n)
    	{
    		For(j,2)
    		{
    			get_init(p,i,1);
    			s[++p]=sp_char;cat[p]=0;
    		}
    	}
    	s[++p]=-1; 
    
    	S.mem(s,p);
    	S.build_sa(Sigma_size);
    	S.make_height();
    	
    	For(i,m)
    	{
    		p=-1;
    		get_init(p,i,0);
    		s[++p]=-1;
    	
    		int K=S.find(s,p);
    		if (K==-1) 
    		{
    			cout<<"0
    ";
    			continue;
    		}
    		int ans=0;
    	
    	
    	
    		int t=S.sa[K];
    		if (flag[cat[t]]^i) sum[cat[t]]++,flag[cat[t]]=i,++ans;			
    		
    		int k=K;
    		while (k>0&&S.cmp_suffix(s,k-1)==0) 
    		{
    			k--;
    			int t=S.sa[k]; 
    			if (flag[cat[t]]^i) sum[cat[t]]++,flag[cat[t]]=i,++ans;			
    		}
    		k=K;
    		while (k<S.n&&S.cmp_suffix(s,k+1)==0) 
    		{
    			k++;
    			int t=S.sa[k]; 
    			if (flag[cat[t]]^i) sum[cat[t]]++,flag[cat[t]]=i,++ans;			
    		}
    		
    		printf("%d
    ",ans);
    		
    	}
    	
    	printf("%d",sum[1]);
    	Fork(i,2,n) printf(" %d",sum[i]);
    	putchar('
    ');
    	
    	
    	return 0;
    }
    











    版权声明:本文博主原创文章。博客,未经同意不得转载。

  • 相关阅读:
    UVALive 6319 暴力
    UVALive 6322 最大匹配
    uvalive 6323 状态压缩DP
    hdu 3874 树状数组
    hdu 3721 树的直径
    hdu 4258 斜率DP
    组队练习 2011年福州全国邀请赛
    FZU 2041 二分枚举
    Kafka基础教程(三):C#使用Kafka消息队列
    Kafka基础教程(二):Kafka安装
  • 原文地址:https://www.cnblogs.com/hrhguanli/p/4915405.html
Copyright © 2011-2022 走看看