BZOJ_2946_[Poi2000]公共串_后缀数组+二分答案

Posted fcwww

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ_2946_[Poi2000]公共串_后缀数组+二分答案相关的知识,希望对你有一定的参考价值。

BZOJ_2946_[Poi2000]公共串_后缀数组+二分答案

Description

 
       给出几个由小写字母构成的单词,求它们最长的公共子串的长度。
任务:
l        读入单词
l        计算最长公共子串的长度
l        输出结果
 

Input

文件的第一行是整数 n1<=n<=5,表示单词的数量。接下来n行每行一个单词,只由小写字母组成,单词的长度至少为1,最大为2000

Output

仅一行,一个整数,最长公共子串的长度。

Sample Input

3
abcb
bca
acbc

Sample Output


先把所有串拼起来,中间用分隔符分开。
二分答案x,转化为判定是否有一个长度为x的公共子串。
然后把后缀分组,每组内height都大于等于x。
只需要判断是否存在一组出现了所有的原串。
 
代码:
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define N 10050
int ws[N],wa[N],wb[N],wv[N],r[N],sa[N],height[N],rank[N],n,m,T,pos[N],h[10];
char str[N];
void build_suffix_array() {
    m=T+27;
    int i,j,p,*x=wa,*y=wb,*t;
    for(i=0;i<m;i++) ws[i]=0;
    for(i=0;i<n;i++) ws[x[i]=r[i]]++;
    for(i=1;i<m;i++) ws[i]+=ws[i-1];
    for(i=n-1;i>=0;i--) sa[--ws[x[i]]]=i;
    for(p=j=1;p<n;j<<=1,m=p) {
        for(p=0,i=n-j;i<n;i++) y[p++]=i;
        for(i=0;i<n;i++) if(sa[i]-j>=0) y[p++]=sa[i]-j;
        for(i=0;i<n;i++) wv[i]=x[y[i]];
        for(i=0;i<m;i++) ws[i]=0;
        for(i=0;i<n;i++) ws[wv[i]]++;
        for(i=1;i<m;i++) ws[i]+=ws[i-1];
        for(i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i];
        for(t=x,x=y,y=t,x[sa[0]]=0,i=p=1;i<n;i++) {
            if(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+j]==y[sa[i-1]+j]) x[sa[i]]=p-1;
            else x[sa[i]]=p++;
        }
    }
    for(i=1;i<n;i++) rank[sa[i]]=i;
    for(i=p=0;i<n-1;height[rank[i++]]=p)
        for(p?p--:0,j=sa[rank[i]-1];r[i+p]==r[j+p];p++) ;
}
bool full() {
    int i;
    for(i=1;i<=T;i++) if(!h[i]) return 0;
    return 1;
}
bool check(int x) {
    int i,j;
    for(i=1;i<n;i++) {
        if(height[i]<x) {
            memset(h,0,sizeof(h));
        }
        h[pos[sa[i]]]++;
        if(full()) return 1;
    }
    return 0;
}
int main() {
    scanf("%d",&T);
    int i,j;
    for(i=1;i<=T;i++) {
        scanf("%s",str);
        for(j=0;str[j];j++) r[n]=str[j]-‘a‘+1,pos[n++]=i;
        r[n++]=26+i;
    }
    n++;
    build_suffix_array();
    int l=0,ri=n+1;
    while(l<ri) {
        int mid=(l+ri)>>1;
        if(check(mid)) l=mid+1;
        else ri=mid;
    }
    printf("%d\n",l-1);
}

 

以上是关于BZOJ_2946_[Poi2000]公共串_后缀数组+二分答案的主要内容,如果未能解决你的问题,请参考以下文章

[BZOJ2946][Poi2000]公共串 后缀自动机

BZOJ 2946 POI2000 公共串 后缀自动机(多串最长公共子串)

BZOJ 2946: [Poi2000]公共串

bzoj2946 [Poi2000]公共串(SA,SAM)

bzoj 2946 [Poi2000]公共串

[POI2000]公共串 - 后缀数组