【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 }
View Code

 

原文地址:https://www.cnblogs.com/LQLlulu/p/9440481.html