POJ-3294-Life Forms(后缀数组-不小于 k 个字符串中的最长子串)

题意:

给定 n 个字符串,求出现在不小于 k 个字符串中的最长子串。

分析:

将 n 个字符串连起来,中间用不相同的且没有出现在字符串中的字符隔开,求后缀数组。

然后二分答案,将后缀分成若干组,判断每组的后缀是否出现在不小于 k 个的原串中。

如果是大于127, char 是负数, 在计数排序的时候是会出问题的。

这题在输出上WA了很多次。最后下载了数据才找出来的。。。。

// File Name: 3294.cpp
// Author: Zlbing
// Created Time: 2013年09月07日 星期六 16时21分37秒

#include<iostream>
#include<string>
#include<algorithm>
#include<cstdlib>
#include<cstdio>
#include<set>
#include<map>
#include<vector>
#include<cstring>
#include<stack>
#include<cmath>
#include<queue>
using namespace std;
#define CL(x,v); memset(x,v,sizeof(x));
#define INF 0x3f3f3f3f
#define LL long long
#define REP(i,r,n) for(int i=r;i<=n;i++)
#define RREP(i,n,r) for(int i=n;i>=r;i--)
//rank从0开始
//sa从1开始,因为最后一个字符(最小的)排在第0位
//height从2开始,因为表示的是sa[i-1]和sa[i]
const int MAXN=220000;
int rank[MAXN],sa[MAXN],X[MAXN],Y[MAXN],height[MAXN];
int s[MAXN];
int buc[MAXN];
int T[MAXN];
void calheight(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] ; s[i+k] == s[j+k] ; k++);
}
bool cmp(int *r,int a,int b,int l) {
    return (r[a] == r[b] && r[a+l] == r[b+l]);
}
void suffix(int n,int m = 128) {
    int i , l , p , *x = X , *y = Y;
    for(i = 0 ; i < m ; i ++) buc[i] = 0;
    for(i = 0 ; i < n ; i ++) buc[ x[i] = s[i]  ] ++;
    for(i = 1 ; i < m ; i ++) buc[i] += buc[i-1];
    for(i = n - 1; i >= 0 ; i --) sa[ --buc[ x[i] ]] = i;
    for(l = 1,p = 1 ; p < n ; m = p , l *= 2) {
        p = 0;
        for(i = n-l ; i < n ; i ++) y[p++] = i;
        for(i = 0 ; i < n ; i ++) if(sa[i] >= l) y[p++] = sa[i] - l;
        for(i = 0 ; i < m ; i ++) buc[i] = 0;
        for(i = 0 ; i < n ; i ++) buc[ x[y[i]] ] ++;
        for(i = 1 ; i < m ; i ++) buc[i] += buc[i-1];
        for(i = n - 1; i >= 0 ; i --) sa[ --buc[ x[y[i]] ] ] = y[i];
        for(swap(x,y) , x[sa[0]] = 0 , i = 1 , p = 1 ; i < n ; i ++)
            x[ sa[i] ] = cmp(y,sa[i-1],sa[i],l) ? p-1 : p++;
    }
    calheight(n-1);//后缀数组关键是求出height,所以求sa的时候顺便把rank和height求出来
}
int vis[1005];
bool solve(int x,int k,int n)
{
    CL(vis,0);
    vis[0]=1;
    int tot=0;
    if(!vis[T[sa[1]]])
        tot++;
    vis[T[sa[1]]]=1;
    for(int i=2;i<=n;i++)
    {
        if(height[i]<x)
        {
            tot=0;
            CL(vis,0);
            vis[0]=1;
            if(!vis[T[sa[i]]])
                tot++;
            vis[T[sa[i]]]=1;
            continue;
        }
        if(!vis[T[sa[i]]])tot++;
        if(tot>=k)return true;
        vis[T[sa[i]]]=1;
    }
    return false;
}
void print(int x,int k,int n)
{
    CL(vis,0);
    int tot=0;
    vis[0]=1;
    if(!vis[T[sa[0]]])
        tot++;
    vis[T[sa[0]]]=1;
    for(int i=1;i<=n;i++)
    {
        if(height[i]<x)
        {
            if(tot>=k)
            {
                for(int j=0;j<x;j++)
                    printf("%c",s[sa[i-1]+j]-6);
                printf("\n");
            }
            tot=0;
            CL(vis,0);
            vis[0]=1;
            if(!vis[T[sa[i]]])
                tot++;
            vis[T[sa[i]]]=1;
            continue;
        }
        if(!vis[T[sa[i]]])tot++;
        vis[T[sa[i]]]=1;
    }
    //这里一开始没写导致WA了很多次
    if(tot>=k)
    {
        for(int j=0;j<x;j++)
            printf("%c",s[sa[n]+j]-6);
        printf("\n");
    }
}
int main() {
    //freopen("C.dat","r",stdin);
    //freopen("Cout.dat","w",stdout);
    int N;
    char ch[1105];
    int first=0;
    while(~scanf("%d",&N))
    {
        if(!N)break;
        if(first)printf("\n");
        first++;
        int k=N/2+1;
        int n=0;
        int tt=1;
        int L=1,R=0;
        REP(i,1,N)
        {
            scanf("%s",ch);
            int len=strlen(ch);
            R=max(R,len);
            REP(j,0,len-1)
            {
                s[n++]=(int)ch[j]+6;
                T[n-1]=i;
            }
            s[n++]=tt++;
            T[n-1]=0;
        }
        //printf("case %d:",first);
        if(N==1)
        {
            printf("%s\n",ch);
            continue;
        }
        s[n-1]=0;
        T[n-1]=0;
        n--;
        suffix(n+1,200);
        int ans=-1;
        while(L<=R)
        {
            int mid=L+(R-L+1)/2;
            if(solve(mid,k,n))
            {
                ans=max(ans,mid);
                L=mid+1;
            }
            else R=mid-1;
        }
        //printf("%d\n",ans);
        if(ans!=-1)
        print(ans,k,n);
        else printf("?\n");
    }
    return 0;
}

 

posted @ 2013-09-08 20:18  z.arbitrary  阅读(211)  评论(0编辑  收藏  举报