1. 程式人生 > >[BZOJ3230] 相似字串 後綴數組+RMQ

[BZOJ3230] 相似字串 後綴數組+RMQ

封裝 小寫字母 代碼 不同 != style sample span ons

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