BZOJ 3230:相似子串
3230: 相似子串
Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 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
ababa
3 5
5 9
8 10
Sample Output
18
16
-1
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