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个字符串第一关键字排第几 
		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] 第二关键字排i的是第?字符串 
			
			Rep(i,m) c[i]=0;
			Rep(i,n) c[x[y[i]]]++; // x[y[i]] 第二关键字排i的字符串,第1关键字排第几 
			For(i,m-1) c[i]+=c[i-1];
			RepD(i,n-1) sa[--c[x[y[i]]]]=y[i]; //第二关键字从大到小遍历,以第一关键字排序 ,最后得到以1,2关键字排序的 
			//此时sa为第一关键字,第2关键字均排好序的 
			swap(x,y);  //此时y变为第一关键字排序的rank,目标是把x变成第一,二关键字排序的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\n";
			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\n",ans);
		
	}
	
	printf("%d",sum[1]);
	Fork(i,2,n) printf(" %d",sum[i]);
	putchar('\n');
	
	
	return 0;
}











你可能感兴趣的:(BZOJ 2754([SCOI2012]喵星球上的点名-后缀数组统计序列集合中子序列出现次数))