码迷,mamicode.com
首页 > 编程语言 > 详细

BZOJ4698 [SDOI2008] Sandy的卡片 - 后缀数组,二分

时间:2018-02-12 21:43:03      阅读:217      评论:0      收藏:0      [点我收藏+]

标签:二分   eof   for   name   clu   i++   using   syn   str   

题意:求在N个串中都出现的最长子串 的长度

很容易想到二分转化为判定性问题。考虑长度M,我们按照长度M进行分组,每个组内进行答案验证,即检查组内是否有N个串的后缀都出现。

时间复杂度O(LlogM)

其实是个经典题。

二分时候记得初始化!

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 struct SA {
 5     int str[2100005];
 6     int x[2100005],y[2100005],u[2100005],v[2100005],r[2100005],o[2100005],hei[2100005],m=2100000,n,a[2100005];
 7     void build(int p,int l,int r){
 8         if(l==r) a[p]=hei[l];
 9         else build(p*2,l,(l+r)/2),build(p*2+1,(l+r)/2+1,r),a[p]=min(a[p*2],a[p*2+1]);
10     }
11     int query(int p,int l,int r,int ql,int qr){
12         if(l>qr||r<ql) return 1e+9;
13         if(l>=ql&&r<=qr) return a[p];
14         return min(query(p*2,l,(l+r)/2,ql,qr),query(p*2+1,(l+r)/2+1,r,ql,qr));
15     }
16     void calc(){
17         int i,j,k=0;
18         for(i=1;i<=n;hei[r[i++]]=k)
19             for(k?k--:0,j=x[r[i]-1];str[i+k]==str[j+k];k++);
20     }
21     int solve(){
22         memset(r,0,sizeof r);
23         for(int i=1;i<=n;i++) u[str[i]]++;
24         for(int i=1;i<=m;i++) u[i]+=u[i-1];
25         for(int i=n;i>=1;i--) x[u[str[i]]--]=i;
26         r[x[1]]=1;
27         for(int i=2;i<=n;i++) r[x[i]]=r[x[i-1]]+((str[x[i]]-str[x[i-1]])?1:0);
28         for(int l=1;r[x[n]]<n;l<<=1) {
29             memset(u,0,sizeof u); memset(v,0,sizeof v); memcpy(o,r,sizeof r);
30             for(int i=1;i<=n;i++) u[r[i]]++, v[(i+l<=n)?r[i+l]:0]++;
31             for(int i=1;i<=n;i++) u[i]+=u[i-1], v[i]+=v[i-1];
32             for(int i=n;i>=1;i--) y[v[(i+l<=n)?r[i+l]:0]--]=i;
33             for(int i=n;i>=1;i--) x[u[r[y[i]]]--]=y[i];
34             r[x[1]]=1;
35             for(int i=2;i<=n;i++) r[x[i]]=r[x[i-1]]+
36                 ((o[x[i]]!=o[x[i-1]])||(((x[i]+l<=n)?o[x[i]+l]:0)!=((x[i-1]+l<=n)?o[x[i-1]+l]:0)));
37         }
38         calc();
39         hei[1]=0;
40     }
41     int lcp(int pos1,int pos2) {return query(1,1,n,min(r[pos1],r[pos2])+1,max(r[pos1],r[pos2]));}
42 } sa;
43 
44 int n,k;
45 int len[2100005],str[2100005],src[2100005],x[1005],delta;
46 
47 int main(){
48     ios::sync_with_stdio(false);
49     cin>>n;
50     for(int i=1;i<=n;i++) {
51         cin>>len[i];
52         for(int j=1;j<=len[i];j++) {
53             cin>>str[j];
54         }
55         for(int j=len[i];j>=2;j--){
56             str[j]-=str[j-1];
57         }
58         for(int j=1;j<=len[i];j++){
59             str[j]+=1000100;
60         }
61         sa.str[len[i-1]]=i;
62         memcpy(sa.str+len[i-1]+1,str+1,len[i]*sizeof(int));
63         len[i]+=len[i-1]+1;
64         
65     }
66     sa.n=len[n]-1;
67     sa.solve();
68     for(int i=1;i<=n;i++)
69         for(int j=len[i-1]+1;j<=len[i]-1;j++)
70             src[j]=i;
71     int left=1,right=len[n]-1,ans;
72     while(left-right){
73         memset(x,0,sizeof x);
74         int mid=(left+right)/2;
75         for(int i=1;i<=len[n]-1;i++) {
76             if(sa.hei[i]<mid-1) {
77                 int flag=1;
78                 for(int j=1;j<=n;j++) 
79                     if(x[j]==0) {
80                         flag=0;
81                         break;
82                     }
83                 if(flag) {
84                     ans=mid;
85                     break;
86                 }
87                 memset(x,0,sizeof x);
88             }
89             x[src[sa.x[i]]]++;
90         }
91         if(ans==mid) left=mid+1;
92         else right=mid;
93     }
94     printf("%d\n",ans);
95 }

 

BZOJ4698 [SDOI2008] Sandy的卡片 - 后缀数组,二分

标签:二分   eof   for   name   clu   i++   using   syn   str   

原文地址:https://www.cnblogs.com/mollnn/p/8443396.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!