bzoj2754 喵星球上的点名 后缀数组

    好久不写后缀树组了(好像以前也就写过一遍吧),各种奇葩出错。

    实际上即使是后缀树组时间复杂度也是不对的。。为O(M*Len)。另外AC自动机也可做,时间复杂度还是不对。。(可能可以用树上的一些结构来统计保证时间复杂度,但具体没有想过)。

    首先将姓名和点名串用间隔符连起来跑后缀树组(sam应该也行,但是后面完全不一样)。把点名串也加进去的话方便找到点名串的位置。找到点名串的位置以后只要height值>点名串的长度就可以不断向前后拓展,然后更新答案。

AC代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 300005
using namespace std;

int n,m,cnt,mx,sa[N],hgt[N],rnk[N],t[N],sum[N*4],blg[N],a[N],sta[N],val[N],l[N],ans[N];
int read(){
	int x=0; char ch=getchar();
	while (ch<'0' || ch>'9') ch=getchar();
	while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }
	return x;
}
void getsa(){
	int i,k,now=0;
	for (i=1; i<=n; i++) sum[a[i]]++;
	for (i=1; i<=mx; i++) sum[i]+=sum[i-1];
	for (i=n; i; i--) sa[sum[a[i]]--]=i;
	for (i=1; i<=n; i++){ rnk[sa[i]]=rnk[sa[i-1]]; if (i<2 || a[sa[i]]!=a[sa[i-1]]) rnk[sa[i]]++; }
	for (k=1; now<n; k<<=1){
		for (i=1; i<=k; i++) t[i]=n-k+i;
		for (i=1; i<=n; i++) sum[i]=0;
		for (i=1; i<=n-k; i++) sum[rnk[i+k]]++;
		sum[1]+=k; for (i=2; i<=n; i++) sum[i]+=sum[i-1];
		for (i=n; i>k; i--) t[sum[rnk[i]]--]=i-k;
		for (i=1; i<=n; i++) sum[i]=0;
		for (i=1; i<=n; i++) sum[rnk[i]]++;
		for (i=2; i<=n; i++) sum[i]+=sum[i-1];
		for (i=n; i; i--) sa[sum[rnk[t[i]]]--]=t[i];
		now=0; memcpy(t,rnk,sizeof(rnk));
		for (i=1; i<=n; i++){
			if (i==1 || t[sa[i]]!=t[sa[i-1]] || t[sa[i]+k]!=t[sa[i-1]+k]) now++; rnk[sa[i]]=now;
		}
	}
}
void gethgt(){
	int i,j,k=0;
	for (i=1; i<=n; i++){
		if (k) k--; if (rnk[i]==1){ hgt[1]=0; continue; }
		for (j=sa[rnk[i]-1]; a[i+k]==a[j+k]; k++); hgt[rnk[i]]=k;
	}
}
int solve(int k,int x){
	int c=blg[sa[x]]; if (!c || val[c]==k) return 0;
	val[c]=k; ans[c]++; return 1;
}
int main(){
	cnt=read(); m=read(); int i,j; mx=1000000;
	for (i=1; i<=cnt; i++) for (j=0; j<2; j++){
		int slen=read(); while (slen--){ a[++n]=read(); blg[n]=i; }
		a[++n]=++mx;
	}
	for (i=1; i<=m; i++){
		int slen=read(); l[i]=slen; sta[i]=n+1;
		while (slen--) a[++n]=read(); a[++n]=++mx;
	}
	getsa(); gethgt(); 
	for (i=1; i<=m; i++){
		int tmp=0;
		for (j=rnk[sta[i]]; j; j--){ tmp+=solve(i,j); if (hgt[j]<l[i]) break; }
		for (j=rnk[sta[i]]+1; j<=n && hgt[j]>=l[i]; j++) tmp+=solve(i,j);
		printf("%d\n",tmp);
	}
	for (i=1; i<cnt; i++) printf("%d ",ans[i]); printf("%d\n",ans[cnt]);
	return 0;
}

by lych

2016.2.11

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值