POJ 3294 出现在至少K个字符串中的子串

在掌握POJ 2774(两个串求最长公共子串)以及对Height数组分组后,本题还是容易想出思路的。

首先用字符集外的不同字符连接所有串,这是为了防止两个后缀在比较时超过某个字符串的分界。二分子串的长度,扫描height数组,判定是否有某个分组来源与至少K个原字符串(本题要求出现超过n的一半次)。

#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <string.h>
#include <stdio.h>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <cassert>
#include <sstream>
using namespace std;

const int N=2e6+110;

int sa[N];
int t1[N],t2[N],c[N];
int rk[N],height[N];

inline int cmp(int *r,int a,int b,int l){
    return r[a]==r[b]&&r[a+l]==r[b+l];
}
char s[N];
void calcSA (char *s,int n,int m) {
    int i,j,p,*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(j=1;j<=n;j<<=1){
        p=0;
        for(i=n-j;i<n;i++)y[p++]=i;
        for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j; // 排名从小到大,如果pos比j大,则suffix(sa[i]-j)的第二关键字为p
        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]; // 根据第二关键字从大到小,确定新一轮sa
        swap(x,y);
        p=1;x[sa[0]]=0;
        for(i=1;i<n;i++)
            x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
        if(p>=n)break;
        m=p;
    }
}

void calcHeight(char *s,int n) {
    int i,j,k=0;
    for(i=0;i<=n;i++)rk[sa[i]]=i;
    for(i=0;i<n;i++){
        if(k)k--; // h[i]>=h[i-1]-1
        j=sa[rk[i]-1]; // suffix(j)排名在suffix(i)前一位
        while(s[i+k]==s[j+k])k++; // 暴力计算lcp
        height[rk[i]]=k;
    }
}

int belong[N];

vector<int>ans;
bool vis[128];
bool ok(int n,int m,int k) {
    memset(vis,0,sizeof vis);
    int cnt=1;
    vis[belong[sa[1]]]=true;
    vector<int>ret;
    bool push=false;
    for (int i=2;i<=n;i++) {
        if (height[i]<m) {
            memset(vis,0,sizeof vis);
            push=false;
            vis[belong[sa[i]]]=true;
            cnt=1;
        }
        else if (!push){
            if (!vis[belong[sa[i]]]) {
                vis[belong[sa[i]]]=true;
                ++cnt;
            }
            if (cnt>k/2&&!push) {
                push=true;
                ret.push_back(sa[i]);
            }
        }
    }
    //cout<<"go "<<m<<" "<<ret.size()<<endl;
    if (ret.size()>0) {
        ans=ret;
        return true;
    }
    else return false;
}
int main () {
    int n;
    while (scanf("%d",&n)!=EOF,n) {
        int p=0;
        int maxLen=0;
        for (int i=1;i<=n;i++) {
            scanf("%s",s+p);
            int l=strlen(s+p);
            maxLen=max(maxLen,l);
            int np=p+l;
            for (int j=p;j<np;j++) {
                belong[j]=i;
                s[j]+=5; // 这里+5是为了保证插入的分隔符不在字符集中出现,n至多为100,a的ASCII为97
            }
            belong[np]=111;
            p=np;
            s[p++]=i;
        }
        s[--p]=0;
        belong[p]=-1;
        calcSA(s,p+1,550);
        calcHeight(s,p);
        int l=0,r=maxLen,ret=0;
        while (l<=r) {
            int m=(l+r)>>1;
            if (ok(p,m,n)) {
                ret=m;
                l=m+1;
            }
            else
                r=m-1;
        }
        if (ret==0) {
            puts("?\n");
        }
        else {
            for (int i=0;i<ans.size();i++) {
                int beg=ans[i];
                for (int j=0;j<ret;j++) printf("%c",s[beg+j]-5);
                puts("");
            }
            puts("");
        }
    }
    return 0;
}

 

posted @ 2015-09-18 21:47  活在夢裡  阅读(413)  评论(0编辑  收藏  举报