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<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
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<m;i++) c[i]=0;
	for(int i=0;i<n;i++) c[x[i]=s[i]]++;
	for(int i=1;i<m;i++) c[i]+=c[i-1];
	for(int i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
	for(int k=1;;k<<=1)
	{
		int p=0;
		for(int i=n-k;i<n;i++) y[p++]=i;
		for(int i=0;i<n;i++) if(sa[i]>=k) y[p++]=sa[i]-k;
		for(int i=0;i<m;i++) c[i]=0;
		for(int i=0;i<n;i++) c[x[y[i]]]++;
		for(int i=1;i<m;i++) c[i]+=c[i-1];
		for(int i=n-1;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;i++)
			x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?p-1:p++;
		m=p;
		if(p>=n) break;
	}
}
void GetHeight()
{
	int k=0;
	for(int i=0;i<n;i++) rank[sa[i]]=i;
	for(int i=0;i<n-1;i++)
	{
		int j=sa[rank[i]-1];
		if(k>0) k--;
		while(s[i+k]==s[j+k]) k++;
		height[rank[i]]=k;
	}
}
void initRMQ()
{
	for(int i=1;i<n;i++) ST[i][0]=height[i];
	for(int i=1;i<=Log[n-1];i++)
		for(int j=1;j<n-(1<<(i-1));j++)
			ST[j][i]=min(ST[j][i-1],ST[j+(1<<(i-1))][i-1]);
}
int RMQ(int l,int r)
{
	if(l>r) swap(l,r);
	int x=Log[r-l+1];
	return min(ST[l][x],ST[r-(1<<x)+1][x]);
}
int main()
{
	scanf("%d%d%s",&n,&Q,s);
	n++;
	BuildSA(256);
	GetHeight();
	for(int i=1;i<n;i++) num[i]=num[i-1]+(LL)(n-1-sa[i])-(LL)height[i];
	for(int i=1;i<=Q;i++)
	{
		scanf("%lld%lld",&x,&y);
		if(x>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;
}


你可能感兴趣的:(BZOJ3230 相似子串)