POJ 1226 Substrings(后缀数组)

POJ 1226 Substrings(后缀数组)

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

题意:

        给你N个字符串,要你求这样一个最长子串的长度,要求这个子串在每个原始串中或原始串的逆串中都出现过一次.输出该串长度即可.

分析: 

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

类似于SPOJ220:

http://blog.csdn.net/u013480600/article/details/24132973

以及POJ3294:

http://blog.csdn.net/u013480600/article/details/24129135

依然是连接字符串和它的逆串,在连接所有double串.在求height数组,然后二分答案求解.不过有几个需要注意的地方.

1.    分隔符这里记得每次都要取不同的.

2.    当串数=1时,直接输出原始串长.

AC代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=20000+1000;
int nn;//nn是正好超过所有字符串个数一半的整数
int who[maxn];//who[i]表示后缀i属于原始第几个串,如果为0则是人为添加的字符
int ans[maxn];//ans[i]=x表示第i个答案是串s的后缀x
int cnt;//计数答案
int turn=0;
struct SuffixArray
{
    int s[maxn];
    int sa[maxn],rank[maxn],height[maxn];
    int t1[maxn],t2[maxn],c[maxn],n;
    int vis[maxn];//需要初始化为0,vis[i]=3表示后缀i出现在了第3轮
    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,t;
        int ss;
        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<nn) continue;
            turn++; //该轮数+1,表示现在是第turn轮
            ss=0;//表示该轮小组目前统计到了ss个串的前缀
            for(k=i-1;k<j;k++)
            if( (t=who[sa[k]])!=0 )
            if(vis[t]!=turn){ ss++;vis[t]=turn; }

            if(ss>=nn)
            return 1;
        }
        return 0;
    }
    void solve()
    {
        if(nn==1)//注意这里
        {
            printf("%d\n",(n-1)/2);
            return ;
        }
        memset(vis,0,sizeof(vis));
        int min=1,max=n;
        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[1000+100];
    int n,T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        nn=n;
        sa.n=0;
        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]+200;//注意这里是200,不是100
                who[sa.n+j]=i;//归属第几个串
            }
            sa.s[sa.n+len]=2*i;//注意1 分隔符
            who[sa.n+len]=0;//分割符归属0
            sa.n=sa.n+len+1;//串长增加

            for(int j=0;j<len;j++)//连接逆串
            {
                sa.s[sa.n+j]=str[len-1-j]+200;
                who[sa.n+j]=i;//归属第几个串
            }
            sa.s[sa.n+len]=2*i-1;//注意2 分割符
            who[sa.n+len]=0;//分割符归属0
            sa.n=sa.n+len+1;//串长增加

        }
        sa.s[sa.n-1]=0;
        sa.build_sa(328);//注意为什么要用328
        sa.build_height();
        sa.solve();
    }
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值