BZOJ3230 相似子串

这个题思路应该算是比较简单的……

先扫一遍height,算出前i个后缀中含有的不同子串个数,然后在这个和中二分,找到一个排名对应的子串。

然后就是最长公共前缀和最长公共后缀……用两个后缀数组即可完成所有操作。

但是我有两个之前写错了的地方:

1、没有开long long

2、注意算LCP的时候,要算RMQ(rank[p]+1,rank[q]),但如果rank[p],rank[q]大小关系未知,则应该先判断是否应当交换。注意交换的是rank[p]和rank[q],而不是rank[p]+1和rank[q],这里注意特判。

#include
#include
#include
#include
#include
using namespace std;
typedef long long LL;
const int MAXN=100010;
const int INF=1000000000;
int n,Q;
int sa[MAXN],c[MAXN],t1[MAXN],t2[MAXN],rank[MAXN],height[MAXN];
int ST[MAXN][25],Log[MAXN];
int l1[MAXN],r1[MAXN],l2[MAXN],r2[MAXN];
LL ans[MAXN],num[MAXN],x,y;
char s[MAXN];
void BuildSA(int m)
{
	int *x=t1,*y=t2;
	for(int i=0;i=0;i--) sa[--c[x[i]]]=i;
	for(int k=1;;k<<=1)
	{
		int p=0;
		for(int i=n-k;i=k) y[p++]=sa[i]-k;
		for(int i=0;i=0;i--) sa[--c[x[y[i]]]]=y[i];
		swap(x,y);
		p=1,x[sa[0]]=0;
		for(int i=1;i=n) break;
	}
}
void GetHeight()
{
	int k=0;
	for(int i=0;i0) k--;
		while(s[i+k]==s[j+k]) k++;
		height[rank[i]]=k;
	}
}
void initRMQ()
{
	for(int i=1;ir) swap(l,r);
	int x=Log[r-l+1];
	return min(ST[l][x],ST[r-(1<num[n-1]||y>num[n-1]) {l1[i]=-1;continue;}
		l1[i]=lower_bound(num+1,num+n,x)-num;
		r1[i]=sa[l1[i]]+x-num[l1[i]-1]-1+height[l1[i]];
		l1[i]=sa[l1[i]];
		l2[i]=lower_bound(num+1,num+n,y)-num;
		r2[i]=sa[l2[i]]+y-num[l2[i]-1]-1+height[l2[i]];
		l2[i]=sa[l2[i]];
	}
	Log[0]=-1;
	for(int i=1;i<=n;i++) Log[i]=Log[i>>1]+1;
	initRMQ();
	for(int i=1;i<=Q;i++)
		if(l1[i]!=-1)
		{
			int p=rank[l1[i]],q=rank[l2[i]];
			if(p>q) swap(p,q);
			LL x=RMQ(p+1,q);
			if(l1[i]==l2[i]) x=INF;
			x=min(x,(LL)min(r1[i]-l1[i]+1,r2[i]-l2[i]+1));
			ans[i]+=x*x;
		}
	reverse(s,s+n-1);
	BuildSA(256);
	GetHeight();
	initRMQ();
	for(int i=1;i<=Q;i++)
		if(l1[i]!=-1)
		{
			int p=n-2-r1[i],q=n-2-r2[i];
			p=rank[p],q=rank[q];
			if(p>q) swap(p,q);
			LL x=RMQ(p+1,q);
			if(r1[i]==r2[i]) x=INF;
			x=min(x,(LL)min(r1[i]-l1[i]+1,r2[i]-l2[i]+1));
			ans[i]+=x*x;
		}
	for(int i=1;i<=Q;i++)
	{
		if(l1[i]==-1) puts("-1");
		else printf("%lld\n",ans[i]);
	}
	return 0;
}


你可能感兴趣的:(BZOJ)