日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

bzoj3277 串 (后缀数组+二分答案+ST表)

發(fā)布時(shí)間:2023/12/2 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 bzoj3277 串 (后缀数组+二分答案+ST表) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

常見操作:先把所有串都連到一起,但中間加上一個(gè)特殊的符號(hào)(不能在原串中/出現(xiàn)過)作為分割

由于全部的子串就等于所有后綴的所有前綴,那我們對(duì)于每一個(gè)后綴,去求一個(gè)最長的前綴,來滿足這個(gè)前綴在至少K個(gè)原串中出現(xiàn)過

那我們就二分一下這個(gè)前綴的長度。現(xiàn)在的問題就是怎么判斷這個(gè)前綴是否在K個(gè)串中出現(xiàn)過了。

顯然,對(duì)于一個(gè)后綴s的長度為x的前綴,只要某個(gè)后綴t 和s的LCP>=x,就說明x也是t的后綴

我們知道,LCP(x,y)=min{height[rank[y]],height[rank[y]-1],...,height[rank[x]+1]},所以其實(shí)t和s的LCP也是單調(diào)的,滿足條件的是一個(gè)排名區(qū)間

那我們用二分出來這個(gè)區(qū)間的左右端點(diǎn)(用st表預(yù)處理一下height的區(qū)間最小值),然后只要判斷這個(gè)區(qū)間里的后綴是否滿足出現(xiàn)在K個(gè)串中就可以了

我們只要提前處理出來left[i],表示一個(gè)最大的排名,使得sa[left[i]]...sa[i]在不同串中出現(xiàn)次數(shù)>=K,然后判斷我剛二分出來的那個(gè)區(qū)間[l,r],是否l<=left[r]就可以了

復(fù)雜度$O(nlog^2n)$,據(jù)說有點(diǎn)卡常,那稍微優(yōu)化一下

我們第一次二分前綴的長度的時(shí)候,顯然現(xiàn)在在做的的最大的長度一定是>=(上一個(gè)后綴的最大前綴長度-1)的,所以只要像求height那樣推著做就可以了

據(jù)說均攤復(fù)雜度O(nlogn)

(第一次寫后綴數(shù)組各種懵...最后還是全都照著大佬的題解抄的...)

1 #include<bits/stdc++.h> 2 #define pa pair<int,int> 3 #define ll long long 4 using namespace std; 5 const int maxn=2e5+10; 6 7 inline ll rd(){ 8 ll x=0;char c=getchar();int neg=1; 9 while(c<'0'||c>'9'){if(c=='-') neg=-1;c=getchar();} 10 while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar(); 11 return x*neg; 12 } 13 14 int NN,N,M,K; 15 char ch[maxn]; 16 int suf[maxn],rank[maxn],rank1[maxn],cnt[maxn],tmp[maxn]; 17 int h[maxn],left0[maxn],bel[maxn],st[maxn][20],pos[maxn][2]; 18 19 inline void getsuf(){ 20 int i,j,k;M=126; 21 for(i=1;i<=N;i++) cnt[ch[i]]=1; 22 for(i=1;i<=M;i++) cnt[i]+=cnt[i-1]; 23 for(i=N;i;i--) rank[i]=cnt[ch[i]]; 24 for(k=1;j!=N;k<<=1){ 25 memset(cnt,0,sizeof(cnt)); 26 for(i=1;i<=N;i++) cnt[rank[i+k>N?0:i+k]]++; 27 for(i=1;i<=M;i++) cnt[i]+=cnt[i-1]; 28 for(i=N;i;i--) tmp[cnt[rank[i+k>N?0:i+k]]--]=i; 29 memset(cnt,0,sizeof(cnt)); 30 for(i=1;i<=N;i++) cnt[rank[i]]++; 31 for(i=1;i<=M;i++) cnt[i]+=cnt[i-1]; 32 for(i=N;i;i--) suf[cnt[rank[tmp[i]]]--]=tmp[i]; 33 memcpy(rank1,rank,sizeof(rank)); 34 rank[suf[1]]=j=1; 35 for(i=2;i<=N;i++){ 36 int ipk=suf[i]+k>N?0:suf[i]+k,i1pk=suf[i-1]+k>N?0:suf[i-1]+k; 37 if(rank1[ipk]!=rank1[i1pk]||rank1[suf[i]]!=rank1[suf[i-1]]) j++; 38 rank[suf[i]]=j; 39 }M=j; 40 } 41 for(i=1;i<=N;i++) suf[rank[i]]=i; 42 } 43 44 inline void geth(){ 45 //h[1]=1; 46 for(int i=1,j=0;i<=N;i++){ 47 if(rank[i]==1) continue; 48 if(j) j--; 49 int x=suf[rank[i]-1]; 50 while(i+j<=N&&x+j<=N&&ch[i+j]==ch[x+j]) j++; 51 h[rank[i]]=j; 52 } 53 } 54 55 inline void getleft0(){ 56 int n=0; 57 memset(cnt,0,sizeof(cnt)); 58 for(int i=1,j=1;i<=N;i++){ 59 if(ch[suf[i]]=='z'+1) continue; 60 if(!cnt[bel[suf[i]]]) n++; 61 cnt[bel[suf[i]]]++; 62 if(n>=K){ 63 for(;n-(cnt[bel[suf[j]]]==1)>=K;n-=(cnt[bel[suf[j++]]]==0)) cnt[bel[suf[j]]]--; 64 left0[i]=j; 65 } 66 } 67 } 68 69 inline void getst(){ 70 for(int i=N;i;i--){ 71 st[i][0]=h[i]; 72 for(int j=1;st[i+(1<<(j-1))][j-1];j++){ 73 st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]); 74 } 75 } 76 } 77 78 inline int rmq(int l,int r){ 79 int k=log2(r-l+1); 80 return min(st[l][k],st[r-(1<<k)+1][k]); 81 } 82 83 inline bool check(int p,int x){ 84 int l,r,l0,r0; 85 if(h[p+1]<x) r0=p; 86 else{ 87 l=p+1;r=N; 88 while(l<=r){ 89 int m=(l+r)>>1; 90 if(rmq(p+1,m)>=x)r0=m,l=m+1; 91 else r=m-1; 92 } 93 } 94 if(h[p]<x) l0=p; 95 else{ 96 l=2;r=p; 97 while(l<=r){ 98 // printf("%d %d\n",l,r); 99 int m=(l+r)>>1; 100 if(rmq(m,p)>=x) l0=m,r=m-1; 101 else l=m+1; 102 }l0--; 103 } 104 return left0[r0]>=l0; 105 } 106 107 inline void solve(){ 108 for(int i=1;i<=NN;i++){ 109 int k=0;ll ans=0; 110 for(int j=pos[i][0];j<pos[i][1];j++){ 111 if(k) k--; 112 for(;j+k<pos[i][1]&&check(rank[j],k+1);k++); 113 ans=ans+k; 114 } 115 printf("%lld ",ans); 116 }printf("\n"); 117 } 118 119 120 121 int main(){ 122 //freopen("3277.in","r",stdin); 123 // freopen("aa.out","w",stdout); 124 int i,j,k; 125 N=NN=rd(),K=rd(); 126 for(i=1,j=1;i<=N;i++){ 127 pos[i][0]=j; 128 scanf("%s",ch+j);k=strlen(ch+j); 129 ch[j+k]='z'+1; 130 for(int t=j;t<j+k;t++) bel[t]=i; 131 j=j+k+1; 132 pos[i][1]=j-1; 133 }N=j-1; 134 getsuf(); 135 geth(); 136 getleft0(); 137 getst(); 138 solve(); 139 return 0; 140 }

?

轉(zhuǎn)載于:https://www.cnblogs.com/Ressed/p/9677823.html

總結(jié)

以上是生活随笔為你收集整理的bzoj3277 串 (后缀数组+二分答案+ST表)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。