Bzoj3230: 相似子串

题解:后缀数组的特性我们可以通过对于这个位置比较前一个位置的h值来解决以i位置为起点的后缀能产生多少个和前面的串不同的子串 所以问题转化为 求第k大出现的位置 二分找就ok 然后找出来的位置求一下lcp 然后反过来再求一下就ok

#include <bits/stdc++.h>
#define ll long long
const int MAXN=1e5+10;
using namespace std;
char s1[MAXN],s2[MAXN];
int txt[MAXN],sa[MAXN],td[MAXN],rank1[MAXN],rank2[MAXN],t1[MAXN],t2[MAXN];
int dp[MAXN][21];int mu[21];int ma[MAXN];
bool cmp(int f[],int t,int w,int k){return f[t]==f[w]&&f[t+k]==f[w+k];}
ll ans[MAXN];
void Sa(char str[]){
  //  cout<<str<<endl;
    int len=strlen(str);int m=128;
    int *td=t1;int *rank1=t2;
    for(int i=0;i<m;i++)txt[i]=0;
    for(int i=0;i<len;i++)rank1[i]=str[i],txt[str[i]]++;
    for(int i=1;i<m;i++)txt[i]+=txt[i-1];
    for(int i=len-1;i>=0;i--)sa[--txt[str[i]]]=i;
    for(int k=1;k<=len;k=k*2){
	int p=0;
	for(int i=len-k;i<len;i++)td[p++]=i;
	for(int i=0;i<len;i++)if(sa[i]>=k)td[p++]=sa[i]-k;
	for(int i=0;i<m;i++)txt[i]=0;
	for(int i=0;i<len;i++)txt[rank1[i]]++;
	for(int i=1;i<m;i++)txt[i]+=txt[i-1];
	for(int i=len-1;i>=0;i--)sa[--txt[rank1[td[i]]]]=td[i];
	swap(rank1,td);rank1[sa[0]]=0;p=1;
	for(int i=1;i<len;i++)rank1[sa[i]]=cmp(td,sa[i],sa[i-1],k)?p-1:p++;
	if(p==len)return ;
	m=p;
    }
}
int h[MAXN],H[MAXN];ll sum[MAXN];
void hh(char str[]){
    int len=strlen(str);
    for(int i=0;i<len;i++)rank2[sa[i]]=i;
    memset(h,0,sizeof(h));
    memset(H,0,sizeof(H));
    for(int i=0;i<len;i++){
	if(rank2[i]==0)continue;
	int t=sa[rank2[i]-1];int w=i;int k;
	if(i==0||H[i-1]<=1)k=0;
	else k=H[i-1]-1,t+=k,w+=k;
	while(t<len&&w<len){
	    if(str[t]==str[w])k++;
	    else break;
	    t++;w++;
	}
	H[i]=k;h[rank2[i]]=k;
    }
}
void st(int len){
    for(int i=1;i<len;i++)dp[i][0]=h[i];
    for(int j=1;mu[j]<=len;j++){
	for(int i=1;i+mu[j]<=len;i++){
	    dp[i][j]=min(dp[i][j-1],dp[i+mu[j-1]][j-1]);
	}
    }
}
int rmq(int l,int r){
    if(l>r)return MAXN;
    int k=ma[r-l+1];
    return min(dp[l][k],dp[r-mu[k]+1][k]);
}
typedef struct node{
    ll l,r;int lx,rx,ly,ry;
}node;
node que[MAXN];
int q;
int check(ll k,int len){
    int l=1;int r=len-1;
    while(l<=r){
	int mid=(l+r)>>1;
	if(sum[mid-1]<k&&sum[mid]>=k)return mid;
	else if(sum[mid-1]>=k)r=mid-1;
	else l=mid+1;
    }
    return len;
}
void slove(){
    Sa(s1);hh(s1);int len=strlen(s1);st(len);
  //  cout<<s1<<" "<<endl;
 //   for(int i=1;i<len;i++)cout<<sa[i]<<" ";
  //  cout<<endl;
    for(int i=1;i<len;i++){sum[i]=len-1-sa[i]-h[i];}
    //for(int i=1;i<len;i++)cout<<sum[i]<<" ";
    //cout<<endl;
    for(int i=1;i<len;i++)sum[i]+=sum[i-1];
    for(int i=1;i<=q;i++){
//	cout<<que[i].l<<" "<<que[i].r<<endl;
	que[i].lx=check(que[i].l,len);que[i].ly=check(que[i].r,len);
//	cout<<i<<" "<<que[i].lx<<" "<<que[i].ly<<endl;
	if(que[i].lx>=len||que[i].ly>=len){
	    ans[i]=-1;continue;
	}
	que[i].rx=que[i].l-sum[que[i].lx-1]+h[que[i].lx];que[i].ry=que[i].r-sum[que[i].ly-1]+h[que[i].ly];
//	cout<<que[i].rx<<"----"<<que[i].ry<<endl;
	ll k1=min(que[i].rx,min(rmq(que[i].lx+1,que[i].ly),que[i].ry));
//	cout<<k1<<" "<<endl;
	ans[i]+=1ll*k1*k1;
	que[i].rx=(que[i].rx-1)+sa[que[i].lx];
	que[i].lx=que[i].rx-sa[que[i].lx]+1;
	que[i].rx=len-2-que[i].rx;
	que[i].ry=(que[i].ry-1)+sa[que[i].ly];
	que[i].ly=que[i].ry-sa[que[i].ly]+1;
	que[i].ry=len-2-que[i].ry;
//	cout<<que[i].lx<<" "<<que[i].ly<<" "<<que[i].rx<<" "<<que[i].ry<<endl;
    }
}
void slove2(){
    Sa(s2);hh(s2);int len=strlen(s2);st(len);
    for(int i=1;i<=q;i++){
	if(ans[i]==-1)continue;
	int l=rank2[que[i].rx];int r=rank2[que[i].ry];
	if(l>r)swap(l,r);l++;
	ll k1=1ll*min(que[i].lx,min(que[i].ly,rmq(l,r)));
	ans[i]+=1ll*k1*k1;
    }
}
int main(){
    mu[0]=1;for(int i=1;i<=20;i++)mu[i]=mu[i-1]<<1;
    ma[0]=-1;for(int i=1;i<MAXN;i++)if((i&(i-1))==0)ma[i]=ma[i-1]+1;else ma[i]=ma[i-1];
    int n;scanf("%d%d",&n,&q);
    scanf(" %s",s1);int len=strlen(s1);s1[len++]='$';
    for(int i=0;i<len-1;i++)s2[i]=s1[len-i-2];s2[len-1]='$';
//    cout<<s2<<endl;
    for(int i=1;i<=q;i++)scanf("%lld%lld",&que[i].l,&que[i].r);
    slove();slove2();
    for(int i=1;i<=q;i++)printf("%lld\n",ans[i]);
}

 

3230: 相似子串

Time Limit: 20 Sec  Memory Limit: 128 MB
Submit: 1980  Solved: 515
[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'组成

posted @ 2018-06-22 11:48  wang9897  阅读(167)  评论(0编辑  收藏  举报