UVA11107 训练指南Life Forms后缀数组
Posted lqllulu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UVA11107 训练指南Life Forms后缀数组相关的知识,希望对你有一定的参考价值。
题意
输入n(n<=100)个字符串,每个字符串长度<=1000,你的任务是找出一个最长的字符串使得超过一半的字符串都包含这个字符串。
分析
训练指南上后缀数组的一道例题,据说很经典(估计也就是height分组比较常用)。但是训练指南上给出的中文题面真滴坑B啊!书上说,连续出现,我懵逼了好久!
我们把这n个字符串连成一个长的字符串S,且中间用不同的未出现的字符相隔开(为什么隔开我们后面说),比如样例一会变为abcdefg1bcdefgh2cdefghi3。这样每一段是一个原字符串。然后问题转换为,在S字符串中,找出一个最长的字符串,使得至少有n/2的匹配点与这个最长的字符串相匹配,且匹配点分别位于至少n/2段。
求最长的时候我们二分答案,然后判断mid是否满足条件。关键是如何判断。我们回想一下通过height数组求LCP的方法,我们知道LCP(i,j)=RMQ(height,rank[i]+1,rank[j]).这里和这个原理是类似的。我们遍历height数组,当height[i]<mid的时候,就新增一个分组。也就是每个分组内的LCP都是>=mid的。然后判断一下这个分组内的后缀是否符合上面说的那个条件(既分组内至少有n/2个后缀,且后缀分别位于至少n/2段)。如果符合则说明mid是合法的。我们上面说过,不同的原串我们在合成S的时候,之间是用特殊字符隔开的,就是为了保证这里每个LCP都是在一个段内。
当时我还在纠结过,这样只是求出最长长度,但是题目要求输出啊。其实,你只要再跑一下上面判断的过程,把那些符合条件的LCP都输出出来就可以了。
mmp宿舍又又又半夜停电还特么这么多蚊子,感觉最后被劝退ACM的不是自己的菜而是暑假集训期间宿舍的温度····
下面是ac的code
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <iostream> 5 6 using namespace std; 7 const int maxn=100+10; 8 const int maxs=1000+100; 9 char s[maxn*maxs]; 10 int c[maxn*maxs],t[maxn*maxs],t2[maxn*maxs],sa[maxn*maxs]; 11 int n,N; 12 void build_sa(int m){ 13 int *x=t,*y=t2; 14 for(int i=0;i<m;i++)c[i]=0; 15 for(int i=0;i<n;i++)c[x[i]=s[i]]++; 16 for(int i=1;i<m;i++)c[i]+=c[i-1]; 17 for(int i=n-1;i>=0;i--)sa[--c[x[i]]]=i; 18 19 for(int k=1;k<=n;k<<=1){ 20 int p=0; 21 for(int i=n-k;i<n;i++)y[p++]=i; 22 for(int i=0;i<n;i++)if(sa[i]>=k)y[p++]=sa[i]-k; 23 for(int i=0;i<m;i++)c[i]=0; 24 for(int i=0;i<n;i++)c[x[y[i]]]++;//这里为啥是这样?一会结合基数排序想一下 25 for(int i=1;i<m;i++)c[i]+=c[i-1]; 26 for(int i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i]; 27 swap(x,y); 28 p=1,x[sa[0]]=0; 29 for(int i=1;i<n;i++) 30 x[sa[i]]=y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]?p-1:p++; 31 if(p>=n)break; 32 m=p; 33 } 34 } 35 36 int Rank[maxn*maxs],height[maxn*maxs]; 37 void getHeight(){//这里有个bug,sa[0]不能等于0 38 int k=0; 39 for(int i=0;i<n;i++)Rank[sa[i]]=i; 40 for(int i=0;i<n;i++){ 41 if(i==0&&Rank[i]==0) 42 continue; 43 if(k)k--; 44 int j=sa[Rank[i]-1]; 45 while(s[i+k]==s[j+k])k++; 46 height[Rank[i]]=k; 47 } 48 } 49 50 char word[maxs]; 51 int idx[maxn*maxs]; 52 int maxlen; 53 void add(int len,int id){ 54 for(int i=0;i<len;i++){ 55 idx[n]=id; 56 s[n++]=word[i]-‘a‘; 57 } 58 s[n]=26+id; 59 idx[n++]=N; 60 } 61 62 bool good(int L,int R){ 63 if(R-L+1<=N/2) 64 return false; 65 int res=0; 66 int vis[maxn]; 67 memset(vis,0,sizeof(vis)); 68 for(int i=L;i<=R;i++){ 69 int id=idx[sa[i]]; 70 if(!vis[id]&&id!=N){ 71 vis[id]=1; 72 res++; 73 } 74 } 75 if(res>N/2) 76 return true; 77 return false; 78 } 79 80 bool judge(int mid){ 81 int L=0; 82 for(int R=0;R<n;R++){ 83 if(height[R]<mid){ 84 if(good(L,R-1)){ 85 return true; 86 } 87 L=R; 88 } 89 } 90 return false; 91 } 92 93 94 void print(int mid){ 95 int L=0; 96 for(int R=0;R<n;R++){ 97 if(height[R]<mid){ 98 if(good(L,R-1)){ 99 for(int i=0;i<mid;i++){ 100 printf("%c",s[i+sa[L]]+‘a‘); 101 } 102 printf(" "); 103 } 104 L=R; 105 } 106 } 107 } 108 int main(){ 109 int kase=0; 110 while(scanf("%d",&N)!=EOF&&N){ 111 if(kase)printf(" "); 112 kase=1; 113 n=0; 114 for(int i=0;i<N;i++){ 115 scanf("%s",word); 116 int len=strlen(word); 117 maxlen=max(maxlen,len); 118 add(len,i); 119 } 120 if(N==1){ 121 printf("%s ",word); 122 continue; 123 } 124 build_sa(26+N); 125 getHeight(); 126 int L=1,R=maxlen; 127 int ans=-1; 128 while(L<=R){ 129 int M=L+(R-L)/2; 130 if(judge(M)) 131 { 132 ans = M; 133 L=M+1; 134 } 135 else 136 R=M-1; 137 } 138 if(ans==-1){ 139 printf("? "); 140 continue; 141 } 142 print(ans); 143 } 144 return 0; 145 }
以上是关于UVA11107 训练指南Life Forms后缀数组的主要内容,如果未能解决你的问题,请参考以下文章
POJ 3294 UVA 11107 Life Forms 后缀数组