【BZOJ3230】相似子串 后缀数组+二分+RMQ

【BZOJ3230】相似子串

Description

Input

输入第1行,包含3个整数N,Q。Q代表询问组数。
第2行是字符串S。
接下来Q行,每行两个整数i和j。(1≤i≤j)。

Output

输出共Q行,每行一个数表示每组询问的答案。如果不存在第i个子串或第j个子串,则输出-1。

Sample Input

5 3
ababa
3 5
5 9
8 10

Sample Output

18
16
-1

HINT

样例解释
第1组询问:两个子串是“aba”,“ababa”。f = 32 + 32 = 18。
第2组询问:两个子串是“ababa”,“baba”。f = 02 + 42 = 16。
第3组询问:不存在第10个子串。输出-1。

数据范围
N≤100000,Q≤100000,字符串只由小写字母'a'~'z'组成

题解:一开始由于用SA还是SAM,然后看到下面的SOURCE一下子就不用犹豫了。。。这个提示也太明显了~

如何字典序第k小的子串呢?考虑二分,问题就变成了问一个子串的字典序,这个可以直接在height数组上求出,答案就是n-sa[i]-height[i]的前缀和。

然后相似程度就好求了,直接维护正串和反串的SA,用RMQ求出LCP即可。

 

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn=100010;
int n,m,Q;
char str[maxn];
int Log[maxn];
ll s[maxn];
inline ll rd()
{
	ll ret=0,f=1;	char gc=getchar();
	while(gc<'0'||gc>'9')	{if(gc=='-')f=-f;	gc=getchar();}
	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
	return ret*f;
}
struct SA
{
	int ra[maxn],rb[maxn],r[maxn],st[maxn],sa[maxn],rank[maxn],h[maxn],f[18][maxn];
	void build()
	{
		int *x=ra,*y=rb,i,j,k,p;
		for(i=0;i<n;i++)	st[x[i]=r[i]]++;
		for(i=1;i<m;i++)	st[i]+=st[i-1];
		for(i=n-1;i>=0;i--)	sa[--st[x[i]]]=i;
		for(j=p=1;p<n;j<<=1,m=p)
		{
			for(p=0,i=n-j;i<n;i++)	y[p++]=i;
			for(i=0;i<n;i++)	if(sa[i]>=j)	y[p++]=sa[i]-j;
			for(i=0;i<m;i++)	st[i]=0;
			for(i=0;i<n;i++)	st[x[y[i]]]++;
			for(i=1;i<m;i++)	st[i]+=st[i-1];
			for(i=n-1;i>=0;i--)	sa[--st[x[y[i]]]]=y[i];
			for(swap(x,y),i=p=1,x[sa[0]]=0;i<n;i++)
				x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+j]==y[sa[i-1]+j])?p-1:p++;
		}
		for(i=1;i<n;i++)	rank[sa[i]]=i;
		for(i=k=0;i<n-1;h[rank[i++]]=k)
			for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
		for(i=1;i<n;i++)	f[0][i]=h[i];
		for(j=1;(1<<j)<n;j++)	for(i=1;i+(1<<j)-1<n;i++)	f[j][i]=min(f[j-1][i],f[j-1][i+(1<<(j-1))]);
	}
	inline int query(int a,int b)
	{
		if(a==b)	return n-a;
		a=rank[a],b=rank[b];
		if(a>b)	swap(a,b);
		a++;
		int k=Log[b-a+1];
		return min(f[k][a],f[k][b-(1<<k)+1]);
	}
}s1,s2;
int find(ll x)
{
	int l=1,r=n+1,mid;
	while(l<r)
	{
		mid=(l+r)>>1;
		if(s[mid]>=x)	r=mid;
		else	l=mid+1;
	}
	return r;
}
int main()
{
	n=rd(),Q=rd();
	scanf("%s",str);
	int i,c,d,la,lb;
	ll a,b;
	for(i=0;i<n;i++)	s1.r[i]=s2.r[n-i-1]=str[i]-'a'+1;
	n++,m=27,s1.build(),m=27,s2.build(),n--;
	for(i=2;i<=n;i++)	Log[i]=Log[i>>1]+1;
	for(i=1;i<=n;i++)	s[i]=s[i-1]+n-s1.sa[i]-s1.h[i];
	for(i=1;i<=Q;i++)
	{
		a=rd(),b=rd(),c=find(a),la=a-s[c-1]+s1.h[c],d=find(b),lb=b-s[d-1]+s1.h[d];
		if(c==n+1||d==n+1)	printf("-1\n");
		else
		{
			a=min(min(la,lb),s1.query(s1.sa[c],s1.sa[d])),b=min(min(la,lb),s2.query(n-s1.sa[c]-la,n-s1.sa[d]-lb));
			printf("%lld\n",a*a+b*b);
		}
	}
	return 0;
}//5 3 ababa 3 5 5 9 8 10 

 

posted @ 2017-09-17 18:02  CQzhangyu  阅读(247)  评论(0编辑  收藏  举报