bzoj3277 串 (后缀数组+二分答案+ST表)
常见操作:先把所有串都连到一起,但中间加上一个特殊的符号(不能在原串中/出现过)作为分割
由于全部的子串就等于所有后缀的所有前缀,那我们对于每一个后缀,去求一个最长的前缀,来满足这个前缀在至少K个原串中出现过
那我们就二分一下这个前缀的长度。现在的问题就是怎么判断这个前缀是否在K个串中出现过了。
显然,对于一个后缀s的长度为x的前缀,只要某个后缀t 和s的LCP>=x,就说明x也是t的后缀
我们知道,LCP(x,y)=min{height[rank[y]],height[rank[y]-1],...,height[rank[x]+1]},所以其实t和s的LCP也是单调的,满足条件的是一个排名区间
那我们用二分出来这个区间的左右端点(用st表预处理一下height的区间最小值),然后只要判断这个区间里的后缀是否满足出现在K个串中就可以了
我们只要提前处理出来left[i],表示一个最大的排名,使得sa[left[i]]...sa[i]在不同串中出现次数>=K,然后判断我刚二分出来的那个区间[l,r],是否l<=left[r]就可以了
复杂度$O(nlog^2n)$,据说有点卡常,那稍微优化一下
我们第一次二分前缀的长度的时候,显然现在在做的的最大的长度一定是>=(上一个后缀的最大前缀长度-1)的,所以只要像求height那样推着做就可以了
据说均摊复杂度O(nlogn)
(第一次写后缀数组各种懵...最后还是全都照着大佬的题解抄的...)
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 }