【bzoj4310/hdu5030-跳蚤】后缀数组
我真的是。。调了一百年。。
傻逼的人生。。
而且这题好像可以用sam做哎!我Y出了一个奇怪的办法。。
好吧sam是不能做这题的。搞错了。
说说后缀数组好了。。
搞后缀数组
然后我们要二分一个子串,判断是否有一种划分方法,满足划分出来的所有串的最大子串不超过这个串。
二分是第now个后缀
二分第now个多长的前缀
————确定了一个子串
首先,这题具有单调性,而且是求最大串最小,所以我们可以二分答案串。
怎么二分答案串呢,我们不是已经用后缀数组求出了sa数组吗,sa数组表示的串是排过序的,其中每个后缀的前缀子串大小按长度的递增而递增,所以可以在sa数组里面二分。(我是先二分后缀,再二分长度)然后是判断,怎么判断是不是可以划分成至多k个串使他们都不超过二分串。还是在sa上做。如果他的sa位置小于mid,那么不用管,因为它怎么样都是小于二分串的。如果他的sa位置大于等于mid,而且他跟二分串没有LCP,那么这个二分一定没有答案,因为最小二分都使他不符合。除此之外,求出sa位置大于等于mid的所有串跟二分串的LCP,在sa[i]~sa[i]+lcp-1的位置上一定要至少打一个标记,因为不打标记它就会比二分串大了。所以最后我们会得到很多个区间,在这些区间里面至多打k-1个标记,使得每个区间中有含有一个标记。转化成了这样,就很容易做了。貌似是smg区间覆盖之类的问题。排个序,去个重,判断+累加一下就可以了。
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<cmath> 5 #include<iostream> 6 #include<algorithm> 7 using namespace std; 8 9 const int N=100100,Inf=(int)1e9; 10 int K,n,cl,ed,r[N],h[N],t[N],rk[N],Rs[N],sa[N],y[N],wr[N]; 11 char c[N]; 12 13 int minn(int x,int y){return x<y ? x:y;} 14 15 void get_sa(int m) 16 { 17 for(int i=1;i<=cl;i++) rk[i]=c[i]-'a'+1; 18 for(int i=1;i<=m;i++) Rs[i]=0; 19 for(int i=1;i<=cl;i++) Rs[rk[i]]++; 20 for(int i=2;i<=m;i++) Rs[i]+=Rs[i-1]; 21 for(int i=cl;i>=1;i--) sa[Rs[rk[i]]--]=i;//debug 22 23 int ln=1,p=0; 24 while(p<cl) 25 { 26 int k=0; 27 for(int i=cl-ln+1;i<=cl;i++) y[++k]=i; 28 for(int i=1;i<=cl;i++) 29 if(sa[i]>ln) y[++k]=sa[i]-ln; 30 for(int i=1;i<=cl;i++) wr[i]=rk[y[i]]; 31 32 for(int i=1;i<=m;i++) Rs[i]=0; 33 for(int i=1;i<=cl;i++) Rs[wr[i]]++; 34 for(int i=2;i<=m;i++) Rs[i]+=Rs[i-1]; 35 for(int i=cl;i>=1;i--) sa[Rs[wr[i]]--]=y[i];//debug 36 37 for(int i=1;i<=cl;i++) wr[i]=rk[i]; 38 for(int i=cl+1;i<=cl+ln;i++) wr[i]=0; 39 rk[sa[1]]=1; 40 p=1; 41 for(int i=2;i<=cl;i++) 42 { 43 if(wr[sa[i]]!=wr[sa[i-1]] || wr[sa[i]+ln]!=wr[sa[i-1]+ln]) p++; 44 rk[sa[i]]=p; 45 } 46 ln*=2;m=p; 47 // for(int i=1;i<=cl;i++) printf("%d ",rk[i]);printf("\n"); 48 // for(int i=1;i<=cl;i++) printf("%d ",sa[i]);printf("\n"); 49 } 50 sa[0]=rk[0]=0; 51 } 52 53 void get_h() 54 { 55 int k=0; 56 for(int i=1;i<=cl;i++) if(rk[i]!=1) 57 { 58 int j=sa[rk[i]-1]; 59 if(k) k--; 60 while(c[i+k]==c[j+k] && i+k<=cl && j+k<=cl) k++; 61 h[rk[i]]=k; 62 } 63 h[1]=0; 64 } 65 66 bool ok(int ll,int rr) 67 { 68 int k=rr-ll+1; 69 memset(t,0,sizeof(t)); 70 memset(r,0,sizeof(r)); 71 for(int i=rk[ll];i<=cl;i++) 72 { 73 if(i!=rk[ll]) k=minn(k,h[i]); 74 else if(rr==cl) continue; 75 if(k==0) return 0; 76 if(t[sa[i]]==0 || sa[i]+k-1<t[sa[i]]) t[sa[i]]=sa[i]+k-1; 77 } 78 int pl=0,pr=0,cut,ans; 79 for(int i=cl;i>=1;i--) 80 { 81 if(!t[i]) continue; 82 if(!pr) {pr=i;continue;} 83 if(pr<=t[i]) t[i]=0; 84 pr=t[i]; 85 } 86 for(int i=1;i<=cl;i++) if(t[i]) r[t[i]]=i; 87 pl=0,pr=0,cut=0,ans=0; 88 for(int i=cl;i>=1;i--) 89 { 90 if(!r[i]) continue; 91 if(!pl) {pl=r[i],pr=i;cut=r[i];ans++;continue;} 92 if(i<pl) cut=r[i],ans++; 93 if(pl<=i && i<=pr) 94 { 95 if(!(cut>=r[i] && cut<=i)) cut=r[i],ans++; 96 } 97 pl=r[i],pr=i; 98 } 99 if(ans<=K-1) return 1; 100 return 0; 101 } 102 103 int check(int now) 104 { 105 int ll=sa[now]+h[now],rr=cl,mid; 106 while(ll<=rr) 107 { 108 mid=(ll+rr)/2; 109 if(ok(sa[now],mid)) 110 { 111 rr=mid; 112 if(ll==rr) return ll; 113 } 114 else ll=mid+1; 115 } 116 if(ll<=rr) return ll; 117 return 0; 118 } 119 120 int main() 121 { 122 // freopen("a.in","r",stdin); 123 freopen("magic.in","r",stdin); 124 freopen("magic.out","w",stdout); 125 scanf("%d",&K); 126 scanf("%s",c+1); 127 cl=strlen(c+1); 128 get_sa(26); 129 get_h(); 130 int ll=1,rr=cl,mid,now; 131 while(ll<rr) 132 { 133 mid=(ll+rr)/2; 134 now=check(mid); 135 if(now) rr=mid,ed=now; 136 else ll=mid+1; 137 } 138 if(ll) 139 { 140 for(int i=sa[ll];i<=ed;i++) printf("%c",c[i]);printf("\n"); 141 } 142 return 0; 143 }
贴一下代码啦。