BZOJ 3230:相似子串

3230: 相似子串

Time Limit: 20 Sec  Memory Limit: 128 MB
Submit: 1527  Solved: 371
[Submit][Status][Discuss]

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'组成

 

Source

题解:

感觉Source里面已经写的很清楚了...

分别求出正串和反串的后缀数组,然后二分找出第i个和第j个串,查询ij串的最长前缀和最长后缀...

注意ij可能会超出int,所以记得开longlong...

代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
//by NeighThorn
#define int long long
using namespace std; 

const int maxn=100000+5;

int n,q,gs[maxn],wb[maxn],wv[maxn];

long long sum[maxn];

char s[maxn];

struct M{
	
	int sa[maxn],st[maxn][25],ran[maxn],height[maxn];
	
	inline void prework(void){
		memset(sa,0,sizeof(sa));
		memset(ran,0,sizeof(ran));
		memset(height,0,sizeof(height));
	}
	
	inline bool cmp(int *x,int a,int b,int l){
		return x[a]==x[b]&&x[a+l]==x[b+l];
	}
	
	inline void da(int *sa,int *x,int n,int m){
		int i,j,p,*y=wb;
		for(i=0;i<m;i++) gs[i]=0;
		for(i=0;i<n;i++) gs[x[i]]++;
		for(i=1;i<m;i++) gs[i]+=gs[i-1];
		for(i=n-1;~i;i--) sa[--gs[x[i]]]=i;
		for(j=1,p=1;p<n;j<<=1,m=p){
			for(i=n-j,p=0;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<n;i++) wv[i]=x[y[i]];
			for(i=0;i<m;i++) gs[i]=0;
			for(i=0;i<n;i++) gs[wv[i]]++;
			for(i=1;i<m;i++) gs[i]+=gs[i-1];
			for(i=n-1;~i;i--) sa[--gs[wv[i]]]=y[i];
			p=1;swap(x,y);x[sa[0]]=0;
			for(i=1;i<n;i++) x[sa[i]]=cmp(y,sa[i],sa[i-1],j)?p-1:p++;
		}
	}
	
	inline void calheight(int n){
		int i,j,k=0;
		for(i=0;i<=n;i++) ran[sa[i]]=i;
		for(i=0;i<n;height[ran[i++]]=k)
			for(k?k--:233,j=sa[ran[i]-1];s[i+k]==s[j+k];k++);
	}
	
	inline void init(void){
		for(int i=1;i<=n;i++)
			st[i][0]=height[i];
		for(int j=1;j<=20;j++)
			for(int i=1;i+(1<<j-1)<=n;i++)
				st[i][j]=min(st[i][j-1],st[i+(1<<j-1)][j-1]);
	}
	
	inline int lcp(int x,int y){
		if(x==y)
			return n;
		x=ran[x],y=ran[y];
		if(x>y)
			swap(x,y);
		x++;
		int len=y-x+1,k;
		for(k=20;k>=0;k--)
			if(len&(1<<k)||k==0)
				break;
		return min(st[x][k],st[y-(1<<k)+1][k]);
	}
	
}pre,suf;

inline void find(int x,int *sa,int *height,int *ran,int &be,int &en){
	int l=1,r=n;
	while(l<=r){
		int mid=(l+r)>>1;
		if(sum[mid]>=x)
			be=sa[mid],r=mid-1;
		else
			l=mid+1;
	}
	en=be+height[ran[be]]+x-sum[ran[be]-1]-1;
}

inline long long solve(int x,int y){
	if(x>sum[n]||y>sum[n])
		return -1;
	int bex,enx,bey,eny;
	long long ans=0;
	find(x,pre.sa,pre.height,pre.ran,bex,enx);
	find(y,pre.sa,pre.height,pre.ran,bey,eny);
	int lala=min(min(eny-bey+1,enx-bex+1),pre.lcp(bex,bey));
	ans+=1LL*lala*lala;
	lala=min(min(eny-bey+1,enx-bex+1),suf.lcp(n-enx-1,n-eny-1));
	ans+=1LL*lala*lala;
	return ans;
}

signed main(void){
	scanf("%lld%lld%s",&n,&q,s);
	memset(sum,0,sizeof(sum));
	pre.prework(),suf.prework();
	for(int i=0;i<n;i++) pre.ran[i]=s[i],suf.ran[i]=s[n-i-1];
	pre.da(pre.sa,pre.ran,n+1,1111);
	memset(wb,0,sizeof(wb));
	memset(wv,0,sizeof(wv));
	suf.da(suf.sa,suf.ran,n+1,1111);
	pre.calheight(n);
	for(int i=0,j=n-1;i<j;i++,j--)
		swap(s[i],s[j]); 
	suf.calheight(n);
	pre.init(),suf.init();
	for(int i=1;i<=n;i++)
		sum[i]=sum[i-1]+n-pre.sa[i]-pre.height[i];
	for(int i=1,x,y;i<=q;i++)
		scanf("%lld%lld",&x,&y),printf("%lld\n",solve(x,y));
	return 0;
}
//1234

  


By NeighThorn

posted @ 2017-02-12 08:46  NeighThorn  阅读(284)  评论(0编辑  收藏  举报