bzoj2946: [Poi2000]公共串

将所有串连起来,中间加一个分隔符,二分答案,有连续ht>=mid且每个串中都出现一次就合法,否则不合法,一定要将分隔符特判掉,否则=0会输出1。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int read()
{
    char ch=getchar();int f=0;
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') {f=f*10+(ch^48);ch=getchar();}
    return f;
}
char s[10100],temp[2005];int tar[8],now,v[15005],ans,n,T,tot;
int sa1[15005],sa2[15005],rk1[15005],rk2[15005];
int *sa,*SA,*rk,*RK,ht[15005];bool vis[8];
bool check(int x)
{
    memset(vis,0,sizeof(vis));
    tot=1;
    for(int i=T;i;i--)
    {
        if(sa[1]>tar[i])
        {
            vis[i]=1;
            break;
        }
    }
    for(int i=2;i<=n;i++)
    {
        if(s[sa[i]]=='~')
        break;
        if(ht[i]>=x)
        {
            for(int j=T;j;j--)
            {
                if(sa[i]>tar[j])
                {
                    if(!vis[j])
                    {
                        vis[j]=1;
                        tot++;
                        if(tot==T)
                        return 1;
                    }
                    break;
                }
            }
        }
        else
        {
            memset(vis,0,sizeof(vis));
            for(int j=T;j;j--)
            {
                if(sa[i]>tar[j])
                {
                    vis[j]=1;
                    break;
                }
            }
            tot=1;
        }
    }
    return 0;
}
int main()
{
    T=read();int l=0,r=20000;
    for(int i=1;i<=T;i++)
    {
        tar[i]=++now;
        s[now]='~';
        scanf("%s",temp+1);
        n=strlen(temp+1);
        r=min(r,n);
        for(int j=1;j<=n;j++)
        {
            s[++now]=temp[j];
        }
    }
    n=now;
    sa=sa1,SA=sa2;rk=rk1;RK=rk2;
    for(int i=1;i<=n;i++) v[s[i]]++;
    for(int i=1;i<=200;i++) v[i]+=v[i-1];
    for(int i=n;i;i--) sa[v[s[i]]--]=i;
    for(int i=1;i<=n;i++) rk[sa[i]]=rk[sa[i-1]]+(s[sa[i]]!=s[sa[i-1]]);
    for(int k=1;k<=n;k<<=1)
    {
        for(int i=1;i<=n;i++) v[rk[sa[i]]]=i;
        for(int i=n;i;i--) if(sa[i]>k) SA[v[rk[sa[i]-k]]--]=sa[i]-k;
        for(int i=n-k+1;i<=n;i++) SA[v[rk[i]]--]=i;
        for(int i=1;i<=n;i++) RK[SA[i]]=RK[SA[i-1]]+(rk[SA[i]]!=rk[SA[i-1]]||rk[SA[i]+k]!=rk[SA[i-1]+k]);
        swap(rk,RK);swap(sa,SA);
        if(rk[sa[n]]==n)
        break;
    }
    int k=0;
    for(int i=1;i<n;i++)
    {
        while(s[sa[rk[i]]+k]==s[sa[rk[i]-1]+k]) k++;
        ht[rk[i]]=k;
        if(k) k--;
    }
    while(l<=r)
    {
        int mid=l+r>>1;
        if(check(mid))
        {
            ans=mid;
            l=mid+1;
        }
        else r=mid-1;
    }
    cout<<ans;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值