有一个细节不是特别懂,然后的话细节有点多,就是挺难发现的那一种,感谢大佬的博客
1470: 后缀数组4:Life Forms
时间限制: 1 Sec 内存限制: 128 MB
提交: 112 解决: 35
[提交] [状态] [讨论版] [命题人:admin]题目描述
【问题描述】
求n个字符串(长度1000)的最长的一个子串,满足该子串在一半以上(不包括一半)的字符串中出现过,并输出该子串,如果有多个子串满足要求,则按字典序输出所有的子串;(全部都是小写字母)
【输入格式】
输入N(1<=N<=100,还真有为1的数据哟)(每个测试点中数据组数不超过100)
【输出格式】
对于每个测试样例,输出答案。如果有很多,则按字典序输出。如果没有解决方案,至少有一个字母,输出“?”在测试用例之间留下一条空行。
接下来是N个字符串
(有多组数据,N为0时结束)
【样例】
输入:3
abcdefg
bcdefgh
cdefghi
3
xxx
yyy
zzz
0
输出:bcdefg
cdefgh
?
然后的话,我就引用一下罗穗骞大佬和zjw大佬的分析
- 我们把他合并起来了,和上一题的处理方法很相似,但是的话我们的ASCII只有127,所以我们要用一个a数组来保存,才可以跑get_sa
- 因为合并了,所以一定要保证他们不是在同一个串里面,其次的话,这道题有一个比较神奇的概念,就是后缀分组,我们把后缀分成好几组,看看哪一组是满足条件的,记录答案,取最大值
(引自罗穗骞)
- 因为我们要后缀分组,所以的话要开一个bool型的v数组判断他有没有出现过,然后用一个belong数组来记录在哪一串
- 然后的话,不知道有一个就是说get_he里面如果Rank[i]==1,就要直接continue,我也不知道为什么,不然会被卡住
大概就是这些了,剩下的就
代码的实现
(注释版,就是解释了一下数组的意思)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<algorithm> 5 #include<cmath> 6 #include<iostream> 7 using namespace std; 8 int sa[110000],Rank[110000],rsort[110000]; 9 int cnt[110000],pos[110000],height[110000]; 10 int a[110000],belong[110000],v[110];/*因为我们中间有空格,所以的话要开到至少101000*/ 11 /*belong数组记录属于哪一串,v数组是用来判断两个子串是否分别属于未出现过的子串*/ 12 bool cmp(int x,int y,int k){return cnt[x]==cnt[y] && cnt[x+k]==cnt[y+k];} 13 char s[1010]; 14 void get_sa(int n,int m) 15 { 16 int k=1,p=0,len; 17 for(int i=1;i<=n;i++) Rank[i]=a[i]; 18 memset(rsort,0,sizeof(rsort)); 19 for(int i=1;i<=n;i++) rsort[Rank[i]]++; 20 for(int i=1;i<=m;i++) rsort[i]+=rsort[i-1]; 21 for(int i=n;i>=1;i--) sa[rsort[Rank[i]]--]=i; 22 for(int k=1;k<n;k<<=1) 23 { 24 len=0; 25 for(int i=n-k+1;i<=n;i++) pos[++len]=i; 26 for(int i=1;i<=n;i++) if(sa[i]>k) pos[++len]=sa[i]-k; 27 for(int i=1;i<=n;i++) cnt[i]=Rank[pos[i]]; 28 memset(rsort,0,sizeof(rsort)); 29 for(int i=1;i<=n;i++) rsort[cnt[i]]++; 30 for(int i=1;i<=m;i++) rsort[i]+=rsort[i-1]; 31 for(int i=n;i>=1;i--) sa[rsort[cnt[i]]--]=pos[i]; 32 for(int i=1;i<=n;i++) cnt[i]=Rank[i]; 33 p=1; Rank[sa[1]]=1; 34 for(int i=2;i<=n;i++) 35 { 36 if(!cmp(sa[i],sa[i-1],k)) p++; 37 Rank[sa[i]]=p; 38 } 39 if(p==n) break; m=p; 40 } 41 a[0]=0; sa[0]=0; 42 } 43 void get_he(int n) 44 { 45 int j,k=0; 46 for(int i=1;i<=n;i++) 47 { 48 if(Rank[i]==1) continue;/*一定要这样,但是我也不知道为什么没有这个会错*/ 49 j=sa[Rank[i]-1]; 50 if(k) k--; 51 while(a[j+k]==a[i+k]) k++; 52 height[Rank[i]]=k; 53 } 54 } 55 bool check(int mid,int k,int n)/*二分搜索答案*/ 56 { 57 int ans=0; 58 memset(v,0,sizeof(v)); v[0]=1; 59 if(!v[belong[sa[1]]]) ans++;/*没出现过*/ 60 v[belong[sa[1]]]=1; 61 for(int i=2;i<=n;i++) 62 { 63 /*分开两边来判断,只要满足条件就是好的,其实就是分组后缀,看一下哪一组满足条件*/ 64 if(height[i]<mid)/*在左边*/ 65 { 66 memset(v,0,sizeof(v)); v[0]=1; 67 ans=0;/*初始化一下*/ 68 if(!v[belong[sa[i]]]) ans++; 69 v[belong[sa[i]]]=1;/*判断*/ 70 } 71 else/*在右边*/ 72 { 73 if(!v[belong[sa[i]]]) ans++; 74 v[belong[sa[i]]]=1;/*判断*/ 75 } 76 if(ans>=k)return true;/*满足条件*/ 77 } 78 return false; 79 } 80 void putt(int x,int k,int n)/*输出答案*/ 81 { 82 int ans=0; 83 memset(v,0,sizeof(v)); v[0]=1; 84 if(!v[belong[sa[1]]]) ans++;/*没出现过*/ 85 v[belong[sa[1]]]=1; 86 for(int i=2;i<=n;i++) 87 { 88 if(height[i]<x)/*在左边*/ 89 { 90 if(ans>=k) 91 { 92 for(int j=sa[i-1];j<=sa[i-1]+x-1;j++) printf("%c",a[j]+'a'); 93 printf("\n"); 94 }/*已经满足条件了就可以直接输出*/ 95 memset(v,0,sizeof(v)); v[0]=1; 96 ans=0; 97 if(!v[belong[sa[i]]]) ans++; 98 v[belong[sa[i]]]=1;/*判断*/ 99 } 100 else/*在右边*/ 101 { 102 if(!v[belong[sa[i]]]) ans++; 103 v[belong[sa[i]]]=1; 104 } 105 } 106 if(ans>=k)/*符合条件就输出*/ 107 { 108 for(int i=sa[n];i<=sa[n]+x-1;i++) printf("%c",a[i]+'a'); 109 printf("\n"); 110 } 111 } 112 int main() 113 { 114 int t; 115 while(scanf("%d",&t)!=EOF && t) 116 { 117 int n=0,tp=26,tt=t/2+1; 118 for(int i=1;i<=t;i++) 119 { 120 scanf("%s",s+1); 121 for(int j=1;j<=strlen(s+1);j++) 122 { 123 a[++n]=s[j]-'a'; 124 belong[n]=i;/*属于哪一串*/ 125 } 126 a[++n]=tp; tp++;/*把二十六个字母保存到A数组里面,ascii只有127位,所以不可以直接保存*/ 127 } 128 if(t==1)/*只有一个的话直接输出就好了*/ 129 { 130 for(int i=1;i<n;i++) printf("%c",a[i]+'a'); 131 printf("\n\n"); continue; 132 } 133 get_sa(n,256); get_he(n); 134 int l=1,r=n,ans=0; 135 while(l<=r)/*二分找答案*/ 136 { 137 int mid=(l+r)/2; 138 if(check(mid,tt,n)) 139 { 140 ans=mid; 141 l=mid+1; 142 } 143 else r=mid-1; 144 } 145 if(!ans) {printf("?\n\n"); continue;} 146 putt(ans,tt,n); printf("\n"); 147 /*ans是我们最后记录的成立的那个边界范围,就是左右的边界范围*/ 148 } 149 return 0; 150 }
(非注释版,挺好理解的,思路清楚了就是细节的探索咯)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<algorithm> 5 #include<cmath> 6 #include<iostream> 7 using namespace std; 8 int sa[110000],Rank[110000],rsort[110000]; 9 int cnt[110000],pos[110000],height[110000]; 10 int a[110000],belong[110000],v[110]; 11 bool cmp(int x,int y,int k){return cnt[x]==cnt[y] && cnt[x+k]==cnt[y+k];} 12 char s[1010]; 13 void get_sa(int n,int m) 14 { 15 int k=1,p=0,len; 16 for(int i=1;i<=n;i++) Rank[i]=a[i]; 17 memset(rsort,0,sizeof(rsort)); 18 for(int i=1;i<=n;i++) rsort[Rank[i]]++; 19 for(int i=1;i<=m;i++) rsort[i]+=rsort[i-1]; 20 for(int i=n;i>=1;i--) sa[rsort[Rank[i]]--]=i; 21 for(int k=1;k<n;k<<=1) 22 { 23 len=0; 24 for(int i=n-k+1;i<=n;i++) pos[++len]=i; 25 for(int i=1;i<=n;i++) if(sa[i]>k) pos[++len]=sa[i]-k; 26 for(int i=1;i<=n;i++) cnt[i]=Rank[pos[i]]; 27 memset(rsort,0,sizeof(rsort)); 28 for(int i=1;i<=n;i++) rsort[cnt[i]]++; 29 for(int i=1;i<=m;i++) rsort[i]+=rsort[i-1]; 30 for(int i=n;i>=1;i--) sa[rsort[cnt[i]]--]=pos[i]; 31 for(int i=1;i<=n;i++) cnt[i]=Rank[i]; 32 p=1; Rank[sa[1]]=1; 33 for(int i=2;i<=n;i++) 34 { 35 if(!cmp(sa[i],sa[i-1],k)) p++; 36 Rank[sa[i]]=p; 37 } 38 if(p==n) break; m=p; 39 } 40 a[0]=0; sa[0]=0; 41 } 42 void get_he(int n) 43 { 44 int j,k=0; 45 for(int i=1;i<=n;i++) 46 { 47 if(Rank[i]==1) continue; 48 j=sa[Rank[i]-1]; 49 if(k) k--; 50 while(a[j+k]==a[i+k]) k++; 51 height[Rank[i]]=k; 52 } 53 } 54 bool check(int mid,int k,int n) 55 { 56 int ans=0; 57 memset(v,0,sizeof(v)); v[0]=1; 58 if(!v[belong[sa[1]]]) ans++; 59 v[belong[sa[1]]]=1; 60 for(int i=2;i<=n;i++) 61 { 62 if(height[i]<mid) 63 { 64 memset(v,0,sizeof(v)); v[0]=1; 65 ans=0; 66 if(!v[belong[sa[i]]]) ans++; 67 v[belong[sa[i]]]=1; 68 } 69 else 70 { 71 if(!v[belong[sa[i]]]) ans++; 72 v[belong[sa[i]]]=1; 73 } 74 if(ans>=k)return true; 75 } 76 return false; 77 } 78 void putt(int x,int k,int n) 79 { 80 int ans=0; 81 memset(v,0,sizeof(v)); v[0]=1; 82 if(!v[belong[sa[1]]]) ans++; 83 v[belong[sa[1]]]=1; 84 for(int i=2;i<=n;i++) 85 { 86 if(height[i]<x) 87 { 88 if(ans>=k) 89 { 90 for(int j=sa[i-1];j<=sa[i-1]+x-1;j++) printf("%c",a[j]+'a'); 91 printf("\n"); 92 } 93 memset(v,0,sizeof(v)); v[0]=1; 94 ans=0; 95 if(!v[belong[sa[i]]]) ans++; 96 v[belong[sa[i]]]=1; 97 } 98 else 99 { 100 if(!v[belong[sa[i]]]) ans++; 101 v[belong[sa[i]]]=1; 102 } 103 } 104 if(ans>=k) 105 { 106 for(int i=sa[n];i<=sa[n]+x-1;i++) printf("%c",a[i]+'a'); 107 printf("\n"); 108 } 109 } 110 int main() 111 { 112 int t; 113 while(scanf("%d",&t)!=EOF && t) 114 { 115 int n=0,tp=26,tt=t/2+1; 116 for(int i=1;i<=t;i++) 117 { 118 scanf("%s",s+1); 119 for(int j=1;j<=strlen(s+1);j++) 120 { 121 a[++n]=s[j]-'a'; 122 belong[n]=i; 123 } 124 a[++n]=tp; tp++; 125 } 126 if(t==1) 127 { 128 for(int i=1;i<n;i++) printf("%c",a[i]+'a'); 129 printf("\n\n"); continue; 130 } 131 get_sa(n,256); get_he(n); 132 int l=1,r=n,ans=0; 133 while(l<=r) 134 { 135 int mid=(l+r)/2; 136 if(check(mid,tt,n)) 137 { 138 ans=mid; 139 l=mid+1; 140 } 141 else r=mid-1; 142 } 143 if(!ans) {printf("?\n\n"); continue;} 144 putt(ans,tt,n); printf("\n"); 145 } 146 return 0; 147 }