[BZOJ3230] 相似字串 后缀数组+RMQ
Posted LadyLex
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[BZOJ3230] 相似字串 后缀数组+RMQ相关的知识,希望对你有一定的参考价值。
3230: 相似子串
Time Limit: 20 Sec Memory Limit: 128 MB
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‘组成
题解: 首先我们要解决的是本质不同的子串计数问题:
考虑到子串一定是后缀的前缀,我们按照rank顺序添加每个后缀,
那么每添加一个新后缀就会产生n-sa+1个新的前缀(子串)
但是由于lcp的存在,这些子串又和前面的那一串重复的一些
所以最后的计算式是Σn-sa+1-height,当然具体的细节,诸如±1会随代码风格和计算方式略有不同,读者自行修改即可
接着我们考虑,本题其实就是让我们求某两个子串最长公共前缀和最长公共后缀
这样我们可以跑一个SA之后把字串反转再求一套,我们就得到了后缀数组和一个诡异的"前缀数组"
接着我们二分找到子串对应的端点以及后缀,再用rmq求一下lcp区间最小值即可.
代码实现(当时我调到意识模糊于是封装了一下233):
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 typedef long long LL; 6 const int N=100010; 7 int n,xx[N],yy[N],cnt[N],bin[23]; 8 LL num[N]; 9 struct Fleet 10 { 11 int sa[N],height[N],rank[N],f[N][18]; 12 char s[N]; 13 int i,j,k,p,m; 14 inline void get_sa() 15 { 16 int *x=xx,*y=yy;m=256; 17 for(i=0;i<m;++i)cnt[i]=0; 18 for(i=0;i<n;++i)++cnt[x[i]=s[i]]; 19 for(i=1;i<m;++i)cnt[i]+=cnt[i-1]; 20 for(i=n-1;~i;--i)sa[--cnt[x[i]]]=i; 21 for(k=1,p=0;p<n&&k<=n;k<<=1,m=p) 22 { 23 for(p=0,i=n-k;i<n;++i)y[p++]=i; 24 for(i=0;i<n;++i)if(sa[i]>=k)y[p++]=sa[i]-k; 25 for(i=0;i<m;++i)cnt[i]=0; 26 for(i=0;i<n;++i)++cnt[x[y[i]]]; 27 for(i=1;i<m;++i)cnt[i]+=cnt[i-1]; 28 for(i=n-1;~i;--i)sa[--cnt[x[y[i]]]]=y[i]; 29 for(swap(x,y),p=1,x[sa[0]]=0,i=1;i<n;++i) 30 x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?p-1:p++; 31 } 32 } 33 inline void get_rank() 34 { 35 for(i=0;i<n;++i)rank[sa[i]]=i; 36 for(k=i=0;i<n;height[rank[i++]]=k) 37 for(k=k?k-1:k,j=sa[rank[i]-1];s[i+k]==s[j+k];++k); 38 } 39 inline void ST() 40 { 41 for(i=0;i<n;++i)f[i][0]=height[i]; 42 for(i=1;bin[i]<=n;++i) 43 for(j=0;j+bin[i]-1<n;++j) 44 f[j][i]=min(f[j][i-1],f[j+bin[i-1]][i-1]); 45 } 46 inline LL query(int l,int r) 47 { 48 int len=r-l+1,k=0; 49 while(bin[k+1]<=len)++k; 50 return min(f[l][k],f[r-bin[k]+1][k]); 51 } 52 inline void get_kth(int &st,int &ed,LL rk) 53 { 54 int ans=lower_bound(num,num+n,rk)-num; 55 st=sa[ans],ed=st+height[ans]-1+rk-num[ans-1]; 56 } 57 inline void intn() 58 {get_sa(),get_rank(),ST();} 59 inline void calc() 60 {for(i=0;i<n;++i)num[i]=num[i-1]+LL(n-sa[i]-height[i]-1);} 61 }Sfx,Pre; 62 inline LL get_length(LL id1,LL id2) 63 { 64 register int i,j,k,st[2],ed[2]; 65 Sfx.get_kth(st[0],ed[0],id1); 66 Sfx.get_kth(st[1],ed[1],id2); 67 LL val=min(ed[0]+1ll-st[0],ed[1]+1ll-st[1]),tmp=val; 68 if(st[0]!=st[1]) 69 if(Sfx.rank[st[0]]<Sfx.rank[st[1]]) 70 tmp=min(tmp,Sfx.query(Sfx.rank[st[0]]+1,Sfx.rank[st[1]])); 71 else 72 tmp=min(tmp,Sfx.query(Sfx.rank[st[1]]+1,Sfx.rank[st[0]])); 73 ed[0]=n-2-ed[0],ed[1]=n-2-ed[1]; 74 if(ed[0]!=ed[1]) 75 if(Pre.rank[ed[0]]<Pre.rank[ed[1]]) 76 val=min(val,Pre.query(Pre.rank[ed[0]]+1,Pre.rank[ed[1]])); 77 else 78 val=min(val,Pre.query(Pre.rank[ed[1]]+1,Pre.rank[ed[0]])); 79 return tmp*tmp+val*val; 80 } 81 int main() 82 { 83 register int i,j,m,a,b,q;LL u,v; 84 scanf("%d%d%s",&n,&m,Sfx.s); 85 for(bin[0]=i=1;i<=20;++i)bin[i]=bin[i-1]<<1; 86 for(i=1;i<=n;++i)Pre.s[i-1]=Sfx.s[n-i]; 87 Sfx.s[n]=Pre.s[n]=1,n++; 88 Sfx.intn();Pre.intn();Sfx.calc(); 89 while(m--) 90 { 91 scanf("%lld%lld",&u,&v); 92 if(u>num[n-1]||v>num[n-1])printf("-1\n"); 93 else printf("%lld\n",get_length(u,v)); 94 } 95 }
以上是关于[BZOJ3230] 相似字串 后缀数组+RMQ的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ 2780: [Spoj]8093 Sevenk Love Oimaster( 后缀数组 + 二分 + RMQ + 树状数组 )