BZOJ 3172: [Tjoi2013]单词|后缀数组

这个ac自动机做法貌似很显然

然而我用的后缀数组(专业制造麻烦……其实就是为了练后缀数组)

这题二分答案会有很多坑爹的地方

原因就是这height数组存的是suffix(sa[i])和suffix(sa[i-1])的LCP

二分答案的时候要注意

不过貌似直接暴力找也挺快  

orz ws_fqk 暴力虐二分……

#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<iostream>
#include<algorithm>
#define T 1111111
using namespace std;
char s[T];
int t1[T],t2[T],cc[T],rank[T],sa[T],height[T];
int len,n,st[T],L[T],h[T][20];
bool cmp(int *y,int a,int b,int k)
{
	int a1=y[a],b1=y[b];
	int a2=a+k>=len?-1:y[a+k];
	int b2=b+k>=len?-1:y[b+k];
	return a1==b1&&a2==b2;
}
void make_sa()
{
	int *x=t1,*y=t2,m=433;
	for(int i=0;i<m;i++)cc[i]=0;
	for(int i=0;i<len;i++)++cc[x[i]=s[i]];
	for(int i=1;i<m;i++)cc[i]+=cc[i-1];
	for(int i=len-1;~i;i--)sa[--cc[x[i]]]=i;
	for(int k=1;k<len;k<<=1)
	{
		int p=0;
		for(int i=len-k;i<len;i++)y[p++]=i;
		for(int i=0;i<len;i++)if(sa[i]>=k)y[p++]=sa[i]-k;
		for(int i=0;i<m;i++)cc[i]=0;
		for(int i=0;i<len;i++)++cc[x[y[i]]];
		for(int i=1;i<m;i++)cc[i]+=cc[i-1];
		for(int i=len-1;~i;i--)sa[--cc[x[y[i]]]]=y[i];
		swap(x,y),m=1,x[sa[0]]=0;
		for(int i=1;i<len;i++)x[sa[i]]=cmp(y,sa[i],sa[i-1],k)?m-1:m++;
		if(m>=len)return;
	}
}
void make_height()
{
	int k=0;
	for(int i=0;i<len;i++)rank[sa[i]]=i;
	for(int i=0;i<len;i++)
	{
		if(!rank[i])continue;
		int j=sa[rank[i]-1];
		if(k)k--;
		while(s[i+k]==s[j+k])k++;
		height[rank[i]]=k;
		//cout <<i <<" "<< rank[i] << " "<< k << endl;
	}
}
void make_stt()
{
	for(int i=0;i<len;i++)h[i][0]=height[i];//cout << height[i] <<" ";
	for(int k=1;(1<<k)<=len;k++)
	    for(int i=0;i<len;i++)
	    {
	    	if(i+(1<<k)>len)break;
	    	h[i][k]=min(h[i][k-1],h[i+(1<<k-1)][k-1]);
	    }
}
int ask(int l,int r)
{
	if(l>r)return T;
	int k=log2(r-l+1);
	int mn=min(h[l][k],h[r-(1<<k)+1][k]);
	return mn;
}
int ask_pre(int R,int x)
{
	int l=1,r=R,ans=R+1;
	while(l<=r)
	{
		int mid=l+r>>1;
		if(ask(mid,R)>=x)ans=mid,r=mid-1;
		else l=mid+1;
	}
	return ans-1;
}
int ask_nxt(int L,int x)
{
	int l=L+1,r=len-1,ans=L;
	while(l<=r)
	{
		int mid=l+r>>1;
		if(ask(L+1,mid)>=x)ans=mid,l=mid+1;
		else r=mid-1;
	}
	return ans;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%s",s+len);
		st[i]=len;
		L[i]=strlen(s+len);
		len+=L[i];
		s[len]='$';
		len++;
	}
	make_sa();
	make_height();
	make_stt();
	for(int i=1;i<=n;i++)
	{
		int l=ask_pre(rank[st[i]],L[i]);
		int r=ask_nxt(rank[st[i]],L[i]);
		printf("%d\n",r-l+1);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值