uva11107 后缀数组

给定n个字符串,求长度最大的字符串(不一定是原字符),在超过一半的字符串中连续出现

1.将n个字符串通过特殊字符连接起来,合成一个文本串。题目转化为寻找匹配点的对应字符串个数超过n/2最长字符串

2.二分答案求出最大值。

二分最大值的基础:如果对于x成立,则对于不大于x的所有值都成立,则可以二分最大值

3.判断是否存在长度不小于p的串在超过n/2的不同字符串区域出现

如何判断?

结论:heigh>=len的连续区间内的所有字符串的LCP长度>=len

证明:根据hdight数组的定义可证。

扫描height数组,寻找height值>=len的连续区间,根据结论,LCP长度>=len,且LCP在区间内所有字符串中出现。对区间内每个元素寻找对应原字符串的序号,若不同序号值>n/2,则存在上述字符串。否则不存在。


#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int maxn=1001*100+10;

struct SuffixArray{
    int s[maxn];    //原始字符数组(最后一个字符必须是0,而前面字符必须非0
    int sa[maxn];   //后缀数组
    int rank[maxn]; //名次数组,rank[0]=n-1
    int height[maxn];//height数组
    int t[maxn],t2[maxn],c[maxn];//辅助数组
    int n;//文本串长度

    void clear() { n=0; memset(sa,0,sizeof(sa)); }

    //m为最大字符值加1,调用前需设置好s和n
    void build_sa(int m){
        int i,*x=t,*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=0;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-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+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--;
            int j=sa[rank[i]-1];
            while(s[i+k]==s[j+k]) k++;
            height[rank[i]]=k;
        }
    }
};

const int maxc=100+10; //串的个数
const int maxl=1000+10;//每个串的长度

SuffixArray sa;
int n;
char word[maxl];
int idx[maxn];//该字符对应原字符串编号
int flag[maxc];

//子串[L,R)是否符合要求
bool good(int L,int R){
    memset(flag,0,sizeof(flag));
    if(R-L<=n/2) return false;
    int cnt=0;
    for(int i=L;i<R;i++){
        int x=idx[sa.sa[i]];
        if(x!=n&&!flag[x]) { flag[x]=1;cnt++; }
    }
    return cnt>n/2;
}

void print_sub(int L,int R){
    for(int i=L;i<R;i++)
        printf("%c",sa.s[i]-1+'a');
    printf("\n");
}

//是否存在长度为len的字符串在超过n/2的文本串中出现
bool print_solutions(int len,bool print){
    int L=0;
    for(int R=1;R<=sa.n;R++){
        if(R=sa.n||sa.height[R]<len){//新开一段
            if(good(L,R)){
                if(print) print_sub(sa.sa[L],sa.sa[L]+len);
                else return true;
            }
            L=R;
        }
    }
    return false;
}

void solve(int maxlen){
    if(!print_solutions(1,false)) printf("?\n");
    else{
        int L=1,R=maxlen,M;
        while(L<R){
            M=L+(R-L+1)/2;
            if(print_solutions(M,false)) L=M;
            else R=M-1;
        }
        print_solutions(L,true);
    }
}

//给字符串加上一个字符,属于字符串i
void add(int ch,int i){
    idx[sa.n]=i;
    sa.s[sa.n++]=ch;
}

int main(){
    //freopen("a.txt","r",stdin);
    int kase=0;
    while(scanf("%d",&n)!=EOF){
        if(!n) break;
        if(kase++>0) printf("\n");
        int maxlen=0;
        sa.clear();
        for(int i=0;i<n;i++){
            scanf("%s",word);
            int sz=strlen(word);
            maxlen=max(maxlen,sz);
            for(int j=0;j<sz;j++)
                add(word[j]-'a'+1,i);
            add(100+i,n);//结束字符
        }
        add(0,n);

        if(n==1) printf("%s\n",word);
        else{
            sa.build_sa(100+n);
            sa.build_height();
            solve(maxlen);
        }
    }
    return 0;
}





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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值