山东济南彤昌机械科技有限公司 山东济南江鹏工贸游有限公司

poj3294 Life Forms(后缀数组)

 

【题目链接】

 

  http://poj.org/problem?id=3294 

 

【题意】

 

  多个字符串求出现超过R次的最长公共子串。

 

【思路】

    

       二分+划分height,判定一个组中是否包含不小于R个不同字符串的后缀

       需要注意的有:

     1)      c[]尽量开大,字符范围为“偏移”之后的范围。

     2)      用kase作为标记节省了每次开始新段需要清零的时间。

     3)      因为height是sa[i]与sa[i-1]的关系,所以无论是在can的开始还是在新段开始都需要初始为一个串的情况。

 

【代码】

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<vector>
  4 #include<iostream>
  5 #define FOR(a,b,c) for(int a=(b);a<=(c);a++)
  6 using namespace std;
  7 
  8 const int maxn = 200000+10;
  9 
 10 int s[maxn];
 11 int sa[maxn],c[maxn],t[maxn],t2[maxn];
 12 
 13 void build_sa(int m,int n) {
 14     int i,*x=t,*y=t2;
 15     for(i=0;i<m;i++) c[i]=0;
 16     for(i=0;i<n;i++) c[x[i]=s[i]]++;
 17     for(i=1;i<m;i++) c[i]+=c[i-1];
 18     for(i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
 19     
 20     for(int k=1;k<=n;k<<=1) {
 21         int p=0;
 22         for(i=n-k;i<n;i++) y[p++]=i;
 23         for(i=0;i<n;i++) if(sa[i]>=k) y[p++]=sa[i]-k;
 24         
 25         for(i=0;i<m;i++) c[i]=0;
 26         for(i=0;i<n;i++) c[x[y[i]]]++;
 27         for(i=0;i<m;i++) c[i]+=c[i-1];
 28         for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
 29         
 30         swap(x,y);
 31         p=1; x[sa[0]]=0;
 32         for(i=1;i<n;i++) 
 33             x[sa[i]]=y[sa[i]]==y[sa[i-1]] && y[sa[i]+k]==y[sa[i-1]+k]?p-1:p++;
 34         if(p>=n) break;
 35         m=p;
 36     }
 37 }
 38 int rank[maxn],height[maxn];
 39 void getHeight(int n) {
 40     int i,j,k=0;
 41     for(i=0;i<=n;i++) rank[sa[i]]=i;
 42     for(i=0;i<n;i++) {
 43         if(k) k--;
 44         j=sa[rank[i]-1];
 45         while(s[j+k]==s[i+k]) k++;
 46         height[rank[i]]=k;
 47     }
 48 }
 49 
 50 int T;
 51 char a[maxn];
 52 
 53 int f[200],kase;
 54 vector<int> st;
 55 int can(int limit,int n,int len) {
 56     int cnt=1,ok=0;
 57     st.clear();
 58     f[sa[1]/len]=kase;
 59     for(int i=2;i<=n;i++) {
 60         if(height[i]<limit) {
 61             cnt=1;
 62             f[sa[i]/len]=++kase;               //检查每一个组中 
 63         }
 64         else {
 65             if(f[sa[i]/len]!=kase) {
 66                 f[sa[i]/len]=kase;
 67                 if(cnt>=0) cnt++;
 68                 if(cnt>T/2) {
 69                     ok=1;
 70                     st.push_back(sa[i]);
 71                     cnt=-1;
 72                 }
 73             }
 74         }
 75     }
 76     return ok;
 77 }
 78 void init() {
 79     kase=1;
 80     memset(sa,0,sizeof(sa));
 81     memset(f,0,sizeof(f));
 82 }
 83 int main() {
 84     //freopen("in.in","r",stdin);
 85     //freopen("out.out","w",stdout);
 86     while(scanf("%d",&T)==1 && T) {
 87         init();
 88         int len,n=0;
 89         for(int i=0;i<T;i++) {
 90             scanf("%s",&a);
 91             len=strlen(a);
 92             for(int j=0;j<len;j++) s[n++]=a[j]+100;
 93             s[n++]=i+1;
 94         }
 95         n--;
 96         s[n]=0;
 97         
 98         build_sa(250,n+1);
 99         getHeight(n);
100         
101         int L=0,R=len+1;
102         while(L<R) {
103             int M=L+(R-L+1)/2;
104             if(can(M,n,len+1)) L=M;
105             else R=M-1;
106         }
107         can(L,n,len+1);                        //再调用一次求出st 
108         if(L==0) printf("?\n");
109         else {
110             for(int i=0;i<st.size();i++)  {
111                 for(int j=st[i];(j-st[i]+1)<=L;j++)
112                     printf("%c",s[j]-100);
113                 putchar('\n');
114             }
115         }
116         putchar('\n');
117     }
118     return 0;
119 }

posted on 2015-11-29 11:06  hahalidaxin  阅读(188)  评论(0编辑  收藏  举报