【BZOJ 2946】【POI 2000】公共串【后缀数组】【裸】

Description

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

Input

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

Output

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

题解

今天遇到一题后缀数组(正解 后缀自动机——一定要掌握的看家本领),作为复习就又写了道的裸题。
直接将所有串合在一起,二分答案的长度,按height数组分组判断即可。

一定注意:不能写用rank做变量名!!!

一定注意:不能写用rank做变量名!!!

一定注意:不能写用rank做变量名!!!

代码

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

#define N 10010
#define rep(i,x,y) for(int i = x; i <= y;i++)

char s[N],str[2010];
int sa[N],t[N],t1[N],c[N];

void build(int n)
{
    int *x = t,*y = t1,m = 150;
    rep(i,0,m-1) c[i] = 0;
    rep(i,0,n-1) c[x[i] = s[i]]++;
    rep(i,1,m-1) c[i] += c[i-1];
    for(int i = n-1;i >= 0;i--) sa[--c[x[i]]] = i;

    for(int k = 1;k <= n;k <<= 1)
    {
        int p = 0;
        for(int i = n-k;i < n;i++) y[p++] = i;
        rep(i,0,n-1) if(sa[i] >= k) y[p++] = sa[i]-k;

        rep(i,0,m-1) c[i] = 0;
        rep(i,0,n-1) c[x[y[i]]] ++;
        rep(i,1,m-1) c[i] += c[i-1];
        for(int i = n-1;i >= 0;i--) sa[--c[ x[y[i]] ]] = y[i];

        swap(x,y);
        p = 1;x[sa[0]] = 0;
        rep(i,1,n-1)
            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;
    }
}

int Rank[N],height[N];
void get_height(int n)
{
    int k = 0;
    rep(i,0,n-1) Rank[sa[i]] = i;
    rep(i,0,n-1)
    {
        if(k) k--;
        int j = sa[Rank[i]-1];
        while(s[i+k] == s[j+k]) k++;
        height[Rank[i]] = k;
    }
}

int n,pos;
bool have[7],flag;
int belong[N];
/*
bool check(int k)
{
    for(int i = 1;i <= pos;i++)
        if(height[i] < k || i == pos) 
        {
            flag = true;
            for(int j = 1;j <= n;j++)
                if(!have[j]) {flag = false;break;}
            if(flag) return true;
            memset(have,false,sizeof(have));
        }
        else have[belong[ sa[i] ]] = have[belong[sa[i-1]]] = true;
    return false;
}*/
bool check(int k)
{
    for(int la=1,i=1;i<=pos;i++)
    {
        if(height[i]<k || i==pos)
        {
            for(int j=1;j<=n;j++) have[j]=0;
            for(int j=la;j<=i-1;j++)have[belong[sa[j]]]=1;
            int flag=0;
            for(int j=1;j<=n;j++)if(have[j]==0){flag=1;break;} 
            if(!flag)return 1;
            la=i;
        }
    }
    return 0;
}

int len[10];
int main()
{
    scanf("%d",&n);
    int R = -1;
    pos = 0;
    for(int i = 1;i <= n;i++)
    {
        scanf("%s",str);
        len[i] = strlen(str);
        R = max(R,len[i]);
        for(int j = 0;j < len[i];j++)
        {
            belong[pos] = i;
            s[pos++] = str[j];
        }
        s[pos++] = '#'+i;
    }
    s[pos] = ' ';
    build(pos);
    get_height(pos);

    int ans = 0,L = 0;
    while(L <= R)
    {
        int mid = (L+R)>>1;
        if(check(mid)){L = mid+1;ans = mid;}
        else R = mid-1;
    }
    printf("%d\n",ans);
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值