1. 程式人生 > >【[SDOI2008]Sandy的卡片】

【[SDOI2008]Sandy的卡片】

string while 轉化 san register iostream include 字符 ret

\(mhr\)的暴力幹翻了

這道題做法還是非常好想的

先做一遍差分,在每個串的某尾插入一個特殊字符,再將所有的串拼接在一起

現在的問題就轉化為找到一個最長的公共子串使得其出現了\(n\)次,但是在一個串內出現多次出現只算一次

先考慮一下沒有第二個限制的做法

那就是最簡單的\(SA\)+二分了,就是掃一遍\(height\)數組,之後根據\(height\)進行分組使得一個組內所有的\(height\)都大於等於當前二分出來的\(mid\),之後一個組內有多少個就代表這個子串出現了多少次

現在有了限制,就是不能來自於同一個串

於是我們給每一個\(sa_i\)打一個標記,標記好其來自哪一個數組,之後對於一個組內出現的同一個串內的我們只需要算一次就夠了

代碼

#include<iostream>
#include<cstring>
#include<cstdio>
#define re register
#define LL long long
#define maxn (1000*100+1000+5)*2
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define pt putchar(1)
inline int read()
{
    char c=getchar();int x=0;
    while(c<‘0‘||c>‘9‘) c=getchar();
    while(c>=‘0‘&&c<=‘9‘) x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
int a[maxn],f[maxn],c[maxn];
int tp[maxn],sa[maxn],rk[maxn],tax[maxn],het[maxn],b[maxn];
int n,m,K,r,t;
inline void qsort()
{
    for(re int i=0;i<=K;i++) tax[i]=0;
    for(re int i=1;i<=n;i++) tax[rk[i]]++;
    for(re int i=1;i<=K;i++) tax[i]+=tax[i-1];
    for(re int i=n;i;--i) sa[tax[rk[tp[i]]]--]=tp[i];
}
inline int check(int x)
{
    int num=0,now=1;
    if(f[sa[1]]) num++,b[f[sa[1]]]++;
    for(re int i=2;i<=n;i++)
    {
        if(het[i]<x)
        {
            if(num>=m) return 1;
            for(re int j=now;j<i;j++) b[f[sa[j]]]=0;
            num=0,now=i;
            if(f[sa[i]]) num++,b[f[sa[i]]]++;
            continue;
        }
        if(!b[f[sa[i]]]&&f[sa[i]]) b[f[sa[i]]]++,num++;
    }
    return num>=m;
}
int main()
{
    m=read();
    for(re int i=1;i<=m;i++)
    {
        t=read();r=max(t,r);
        for(re int j=1;j<=t;j++) a[++n]=read(),f[n]=i;
        a[++n]=i+101,f[n]=0;
    }
    for(re int i=1;i<=n;i++) c[i]=a[i]-a[i-1];
    for(re int i=1;i<=n;i++) c[i]+=2001,K=max(c[i],K);
    c[1]=0;
    for(re int i=1;i<=n;i++) tp[i]=i,rk[i]=c[i];
    qsort();
    for(re int w=1,p=0;p<n;K=p,w<<=1)
    {
        p=0;
        for(re int i=1;i<=w;i++) tp[++p]=n-w+i;
        for(re int i=1;i<=n;i++) if(sa[i]>w) tp[++p]=sa[i]-w;
        qsort();
        for(re int i=1;i<=n;i++) std::swap(rk[i],tp[i]);
        rk[sa[1]]=p=1;
        for(re int i=2;i<=n;i++) rk[sa[i]]=(tp[sa[i-1]]==tp[sa[i]]&&tp[sa[i-1]+w]==tp[sa[i]+w])?p:++p;
    }
    int k=0;
    for(re int i=1;i<=n;i++)
    {
        if(k) --k;
        int j=sa[rk[i]-1];
        while(c[i+k]==c[j+k]) ++k;
        het[rk[i]]=k;
    }
    int l=1,ans=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+1);
    return 0;
}

【[SDOI2008]Sandy的卡片】