[SDOI2008]Sandy的卡片(后缀数组求LCP模板,二分)

 [SDOI2008]Sandy的卡片(luogu)

Solution

容易看出“相同”指差分后相同的+1

将所有串差分后得到的串(长度-1)放在一起,两个串间以一个串内未出现且各不相同的数相隔,记录每个数原属的串

后缀数组求height

二分查找最大的数,使有 n 个属于不同原串的点满足height>=这个数

Code

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=1001,M=102,K=N*M;
int gs,n,m,b[K],a[N][M],len[N],ma,mi,l,r,inf=1<<30,id[K],ans;
int rk1[K],rk2[K],rk[K],pre[K],sa[K],cnt[K],he[K];
void init()
{
    scanf("%d",&gs);
    mi=r=inf;
    for(int i=1;i<=gs;i++)
    {
        scanf("%d",&len[i]);
        for(int j=1;j<=len[i];j++)
        {
            scanf("%d",&a[i][j]);
            if(j>1) ma=max(ma,a[i][j]-a[i][j-1]);
        }
        r=min(r,len[i]-1);
    }
    for(int i=1;i<=gs;b[++n]=++ma,i++)
        for(int j=2;j<=len[i];j++)
        {
            b[++n]=a[i][j]-a[i][j-1];
            id[n]=i;
            mi=min(mi,b[n]);
        }
    for(int i=1;i<=n;i++) b[i]-=mi-1,m=max(m,b[i]);
}
void get_sa()
{
    for(int i=1;i<=n;i++) cnt[b[i]]++;
    for(int i=1;i<=m;i++) cnt[i]+=cnt[i-1];
    for(int i=1;i<=n;i++) rk[i]=cnt[b[i]-1]+1;
    for(int mid=1;mid<n;mid<<=1)
    {
        for(int i=1;i<=n;i++)
        {
            rk1[i]=rk[i];
            if(i+mid<=n) rk2[i]=rk[i+mid];
            else rk2[i]=0;
        }
        memset(cnt,0,sizeof(cnt));
        for(int i=1;i<=n;i++) cnt[rk2[i]]++;
        for(int i=1;i<=n;i++) cnt[i]+=cnt[i-1];
        for(int i=n;i;i--) pre[cnt[rk2[i]]--]=i;
        memset(cnt,0,sizeof(cnt));
        for(int i=1;i<=n;i++) cnt[rk1[i]]++;
        for(int i=1;i<=n;i++) cnt[i]+=cnt[i-1];
        for(int i=n;i;i--) sa[cnt[rk1[pre[i]]]--]=pre[i];
        rk[sa[1]]=1;
        for(int i=2;i<=n;i++)
            if(rk1[sa[i]]==rk1[sa[i-1]] && rk2[sa[i]]==rk2[sa[i-1]]) rk[sa[i]]=rk[sa[i-1]];
            else rk[sa[i]]=rk[sa[i-1]]+1;
        if(rk[sa[n]]==n) return ;
    }
}
void get_he()
{
    int k=0;
    for(int i=1;i<=n;i++)
    {
        if(rk[i]==1) continue;
        if(k) k--;
        int j=sa[rk[i]-1];
        while(i+k<=n && j+k<=n && b[i+k]==b[j+k]) k++;
        he[rk[i]]=k;
    }
}
int st[K],vis[K],top;
bool check(int x)
{
    while(top) vis[st[top--]]=0;
    for (int i=1;i<=n;i++)
    {
        if(he[i]<x)
            while (top) vis[st[top--]]=0;
        if(!vis[id[sa[i]]])
        {
            vis[id[sa[i]]]=1;
            st[++top]=id[sa[i]];
            if(top==gs) return 1; 
        }
    }
    return 0;
}
int main()
{
    init();
    get_sa();
    get_he();
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(check(mid)) l=mid+1,ans=mid;
        else r=mid-1;
    }
    printf("%d",ans+1);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/hsez-cyx/p/12424629.html