poj3294 二分+后缀数组

来源:互联网 发布:淘宝平面男模特动作 编辑:程序博客网 时间:2024/06/06 03:08
//题意:给定n个字符串,让你求出在大于n/2个字符串中的最长公共串。//height数组,存的是排名相邻的两个后缀的最长公共前缀。height是按后缀的字典序来的//rank保存的是suffix(i)在所有后缀中从小到大的排列的名次。//后缀数组sa:将str的n个后缀从小到大进行排序之后把排好序的后缀的开头位置顺次放入SA中。#include<stdio.h>#include<string.h>#define max 101000int next[max],sa[max];int wa[max],wb[max],wv[max],ws[max];int rank[max],height[max];int vis[1001];char tem[1001];int str[max],lcp[max],n;char ans[max];int cmp(int *r,int a,int b,int l){    return r[a]==r[b]&&r[a+l]==r[b+l];}void da(int *r,int *sa,int n,int m){    int i,j,p,*x=wa,*y=wb,*t;    for(i=0; i<m; i++) ws[i]=0;    for(i=0; i<n; i++) ws[x[i]=r[i]]++;    for(i=1; i<m; i++) ws[i]+=ws[i-1];    for(i=n-1; i>=0; i--) sa[--ws[x[i]]]=i;    for(j=1,p=1; p<n; j*=2,m=p)    {        for(p=0,i=n-j; i<n; i++) y[p++]=i;        for(i=0; i<n; i++) if(sa[i]>=j)y[p++]=sa[i]-j;        for(i=0; i<n; i++)wv[i]=x[y[i]];        for(i=0; i<m; i++)ws[i]=0;        for(i=0; i<n; i++)ws[wv[i]]++;        for(i=1; i<m; i++) ws[i]+=ws[i-1];        for(i=n-1; i>=0; i--) sa[--ws[wv[i]]]=y[i];        for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1; i<n; i++)            x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;    }    return ;}void calheight(int *r,int *sa,int n){    int i,j,k=0;    for(i=1; i<=n; i++) rank[sa[i]]=i;    for(i=0; i<n; height[rank[i++]]=k)        for(k?k--:0,j=sa[rank[i]-1]; r[i+k]==r[j+k]; k++);    return ;}int check(int mid,int len){    int count=0;    memset(vis,0,sizeof(vis));    for(int i=2; i<=len; i++)    {        if(height[i]<mid)        {            memset(vis,0,sizeof(vis));            count=0;            continue;        }//避免计算重复。        if(!vis[lcp[sa[i]]])  vis[lcp[sa[i]]]=1,count++;        if(!vis[lcp[sa[i-1]]])  vis[lcp[sa[i-1]]]=1,count++;        if(count>n/2) return 1;    }    return 0;}void output(int mid,int len){    int count=0,flag=0;    memset(vis,0,sizeof(vis));    for(int i=2; i<=len; i++)    {        if(height[i]<mid)        {            memset(vis,0,sizeof(vis));            count=0;            flag=0;            continue;        }        if(!vis[lcp[sa[i]]])  vis[lcp[sa[i]]]=1,count++;        if(!vis[lcp[sa[i-1]]])  vis[lcp[sa[i-1]]]=1,count++;        if(count>n/2&&!flag)        {            for(int j=0; j<mid; j++)                ans[j]=str[sa[i]+j]+'a'-1;            ans[mid]='\0';            printf("%s\n",ans);            flag=1;        }    }    return ;}int main(){    int len;    int min;    int sumLen;    while(scanf("%d",&n),n)    {        memset(str,'\0',sizeof(str));        min=max;        sumLen=0;        int m=28;        for(int i=0; i<n; i++)//将字符串合并,记录最小长度值        {            scanf("%s",tem);            len=strlen(tem);            if(min>len) min=len;            for(int j=0; j<len; j++)            {                str[sumLen]=tem[j]-'a'+1;                lcp[sumLen++]=i;            }            str[sumLen]=m;            lcp[sumLen++]=m++;        }        str[sumLen]=0;        da(str,sa,sumLen+1,m);        calheight(str,sa,sumLen);        int left=0,right=min,mid;        int flag=0;        while(right>=left)//二分        {            mid=(right+left)/2;            if(check(mid,sumLen))            {                left=mid+1;                flag=mid;            }            else            {                right=mid-1;            }        }        if(flag)        {            output(flag,sumLen);        }        else            printf("?\n");        printf("\n");    }    return 0;}

原创粉丝点击