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

题意:求在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 }

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

时间: 2024-10-09 06:22:18

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

SDOI2008 Sandy的卡片( 后缀数组 )

求出后缀数组, 然后二分答案, 对height数组分组检验答案. 时间复杂度O(|S| log|S|) -------------------------------------------------------------------------------- #include<cstdio> #include<cctype> #include<cstring> #include<algorithm> using namespace std; const

bzoj4698 / P2463 [SDOI2008]Sandy的卡片

P2463 [SDOI2008]Sandy的卡片 直接二分长度暴力匹配....... 跑的还挺快 (正解是后缀数组的样子) 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 void read(int &x){ 6 char c=getchar();x=0; 7 while(c<'0'||c>'9')c=getchar(); 8 w

BZOJ 4698: Sdoi2008 Sandy的卡片

4698: Sdoi2008 Sandy的卡片 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 225  Solved: 95[Submit][Status][Discuss] Description Sandy和Sue的热衷于收集干脆面中的卡片.然而,Sue收集卡片是因为卡片上漂亮的人物形象,而Sandy则是为了积 攒卡片兑换超炫的人物模型.每一张卡片都由一些数字进行标记,第i张卡片的序列长度为Mi,要想兑换人物模型 ,首先必须要集够N张卡片,对

hdu 5030 Rabbit&#39;s String(后缀数组&amp;二分)

Rabbit's String Time Limit: 40000/20000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 288    Accepted Submission(s): 108 Problem Description Long long ago, there lived a lot of rabbits in the forest. One day, the

BZOJ 3230: 相似子串( RMQ + 后缀数组 + 二分 )

二分查找求出k大串, 然后正反做后缀数组, RMQ求LCP, 时间复杂度O(NlogN+logN) --------------------------------------------------------------------- #include<cstdio> #include<algorithm> #include<cstring> #include<cctype> using namespace std; typedef long long

hdu 5008(2014 ACM/ICPC Asia Regional Xi&#39;an Online ) Boring String Problem(后缀数组&amp;二分)

Boring String Problem Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 219    Accepted Submission(s): 45 Problem Description In this problem, you are given a string s and q queries. For each que

【bzoj4310】跳蚤 后缀数组+二分

题目描述 很久很久以前,森林里住着一群跳蚤.一天,跳蚤国王得到了一个神秘的字符串,它想进行研究. 首先,他会把串分成不超过 k 个子串,然后对于每个子串 S,他会从S的所有子串中选择字典序最大的那一个,并在选出来的 k 个子串中选择字典序最大的那一个.他称其为“魔力串”. 现在他想找一个最优的分法让“魔力串”字典序最小. 输入 第一行一个整数 k. 接下来一个长度不超过 105 的字符串 S. 输出 输出一行,表示字典序最小的“魔力串”. 样例输入 13 bcbcbacbbbbbabbacbcb

WHU---1084 - 连续技 (后缀数组+二分)

Description 不管是什么武功,多少都会有一或两个连续技多次出现,这些连续技常常是发明该武功的人的习惯性动作,如果这些动作被对手分析出来了,就很容易被对手把握住先机.比如松风剑谱里面有一式叫做迎风傲骨是如下的动作: 劈 刺 削 刺 削 踢 刺 削 刺 削 很明显 刺-削 这个连续动作出现了4次,而 刺-削-刺-削 这个连续动作则出现了两次. 现在刘白宇弄到了一本魔教的掌法,想让你帮忙来分析其中最长的且出现尽量多的连续技,当然,他不好意思麻烦你太久,只想让你告诉它这个连续技有多长并且出现了

poj 3261 Milk Patterns 后缀数组+二分

1 /*********************************************************** 2 题目: Milk Patterns(poj 3261) 3 链接: http://poj.org/problem?id=3261 4 题意: 给一串数字,求这些数字中公共子串个数大于k的 5 最长串. 6 算法: 后缀数组+二分 7 ***********************************************************/ 8 #incl