SPOJ 220 . Relevant Phrases of Annihilation(后缀数组)

来源:互联网 发布:linux telnet root登录 编辑:程序博客网 时间:2024/04/28 10:59

SPOJ 220 . Relevant Phrases of Annihilation(后缀数组)

题意:

        给你N个串,要你在这N个串中找出这样一个最长的子串长度.该串在任意一个串中至少出现了2次,并且不重叠的.

分析: 

        罗穗骞《后缀数组——处理字符串的有力工具》例题.

        首先依然连接所有串,并且加上分隔符.然后依然是二分答案,看看长为limit的这个串是否存在.在每个height组内看看是否覆盖了所有串两次且最小位置与最大位置之差>=limit.

AC代码:

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int maxn=100000+1000;int who[maxn];//who[i]表示后缀i属于原始第几个串,如果为0则是人为添加的字符int num;//原始串的个数int min_pos[15],max_pos[15];struct SuffixArray{    int s[maxn];    int sa[maxn],rank[maxn],height[maxn];    int t1[maxn],t2[maxn],c[maxn],n;    void build_sa(int m)    {        int i,*x=t1,*y=t2;        for(i=0;i<m;i++) c[i]=0;        for(i=0;i<n;i++) c[x[i]=s[i]]++;        for(i=1;i<m;i++) c[i]+=c[i-1];        for(i=n-1;i>=0;i--) sa[--c[x[i]]]=i;        for(int k=1;k<=n;k<<=1)        {            int p=0;            for(i=n-k;i<n;i++) y[p++]=i;            for(i=0;i<n;i++)if(sa[i]>=k) y[p++]=sa[i]-k;            for(i=0;i<m;i++) c[i]=0;            for(i=0;i<n;i++) c[x[y[i]]]++;            for(i=1;i<m;i++) c[i]+=c[i-1];            for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];            swap(x,y);            p=1,x[sa[0]]=0;            for(i=1;i<n;i++)                x[sa[i]]=y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]?p-1:p++;            if(p>=n) break;            m=p;        }    }    void build_height()    {        int i,j,k=0;        for(i=0;i<n;i++) rank[sa[i]]=i;        for(i=0;i<n;i++)        {            if(k)k--;            j=sa[rank[i]-1];            while(s[i+k]==s[j+k])k++;            height[rank[i]]=k;        }    }    bool check(int limit)    {        int i,j,k;        for(i=2;i<n;i=j+1)        {            for(;height[i]<limit&&i<n;i++);//i-1是该组下界            for(j=i;height[j]>=limit&&j<n;j++);//j-1是该组上界            if(j-i+1<num) continue;            for(k=1;k<=num;k++){min_pos[k]=10001;max_pos[k]=-1;}            for(k=i-1;k<j;k++)            {                if(sa[k]<min_pos[who[sa[k]]]) min_pos[who[sa[k]]]=sa[k];                if(sa[k]>max_pos[who[sa[k]]]) max_pos[who[sa[k]]]=sa[k];            }            for(k=1;k<=num;k++)if(max_pos[k]-min_pos[k]<limit)break;            if(k>num) return true;        }        return false;    }    void solve()    {        int min=1,max=5000;        while(min<=max)        {            int mid=min+(max-min)/2;            if(check(mid))min=mid+1;            else max=mid-1;        }        printf("%d\n",max);    }}sa;int main(){    char str[10000+100];    int n,T;    scanf("%d",&T);    while(T--)    {        scanf("%d",&n);        sa.n=0;        num=n;        for(int i=1;i<=n;i++)        {            scanf("%s",str);            int len=strlen(str);            for(int j=0;j<len;j++)            {                sa.s[sa.n+j]=str[j]+100;                who[sa.n+j]=i;//归属第几个串            }            sa.s[sa.n+len]=i;//分割符            who[sa.n+len]=0;//分割符归属0            sa.n=sa.n+len+1;//串长增加        }        sa.s[sa.n-1]=0;        sa.build_sa(228);        sa.build_height();        sa.solve();    }}


0 0
原创粉丝点击