BZOJ2946: [Poi2000]公共串

后缀数组 二分

题目传送门

HDU4080一个套路。
先把这几个串接起来,串和串之间用一个符号隔开。二分答案l,当连续一段h[i]的长度都≥l,且sa[i]分布在所有串中就满足。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 10010
using namespace std;
int n,m,ti,sa[N],rk[N],rs[N],t[N],h[N],L[6];
char s[N],ch[N];
void RS(){
    memset(rs,0,sizeof(rs));
    for (int i=1;i<=n;i++) rs[rk[t[i]]]++;
    for (int i=1;i<=m;i++) rs[i]+=rs[i-1];
    for (int i=n;i;i--) sa[rs[rk[t[i]]]--]=t[i];
}
void SA(){
    for (int i=1;i<=n;i++) rk[i]=s[i],t[i]=i; RS();
    for (int j=1,p=0;j<=n;j<<=1,p=0){
        for (int i=n-j+1;i<=n;i++) t[++p]=i;
        for (int i=1;i<=n;i++) if (sa[i]>j) t[++p]=sa[i]-j;
        RS(),memcpy(t,rk,sizeof(t)),p=rk[sa[1]]=1;
        for (int i=2;i<=n;i++)
            rk[sa[i]]=(t[sa[i]]==t[sa[i-1]]&&t[sa[i]+j]==t[sa[i-1]+j])?p:++p;
        if (p==n) break; m=p;
    }
}
void mkh(){
    for (int i=1,k=0;i<=n;i++){
        if (k) k--;
        while (s[i+k]==s[sa[rk[i]-1]+k]) k++;
        h[rk[i]]=k;
    }
}
bool pd(int l){
    int sum=0;
    for (int i=1;i<=n;i++){
        if (h[i]<l) sum=0;
        for (int j=1;j<=ti;j++) if (sa[i]<=L[j]){ sum|=1<<j-1; break; }
        if (sum==(1<<ti)-1) return true;
    }
    return false;
}
int main(){
    scanf("%d",&ti);
    for (int i=1;i<=ti;i++){
        scanf("%s",ch+1),L[i]=strlen(ch+1);
        for (int j=1;ch[j];j++) s[n+j]=ch[j];
        n+=L[i],s[++n]='~',L[i]=n;
    }
    s[n--]=0,m=127,SA(),mkh(); int l=0,r=n,mid,ans=0;
    while (l<=r) if (pd(mid=l+r>>1)) ans=mid,l=mid+1; else r=mid-1;
    return printf("%d\n",ans),0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值